aboutsummaryrefslogtreecommitdiffstats
path: root/system_cmds/iosim.tproj/iosim.c
diff options
context:
space:
mode:
Diffstat (limited to 'system_cmds/iosim.tproj/iosim.c')
-rw-r--r--system_cmds/iosim.tproj/iosim.c673
1 files changed, 673 insertions, 0 deletions
diff --git a/system_cmds/iosim.tproj/iosim.c b/system_cmds/iosim.tproj/iosim.c
new file mode 100644
index 0000000..7a0efce
--- /dev/null
+++ b/system_cmds/iosim.tproj/iosim.c
@@ -0,0 +1,673 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <string.h>
+#include <sys/resource.h>
+#include <pthread.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <math.h>
+#include <signal.h>
+#include <libkern/OSAtomic.h>
+#include <limits.h>
+#include <errno.h>
+#include <CoreFoundation/CoreFoundation.h>
+#include "panic.h"
+#include <IOKit/IOKitLib.h>
+#include <spawn.h>
+
+#define IO_MODE_SEQ 0
+#define IO_MODE_RANDOM 1
+
+#define WORKLOAD_TYPE_RO 0
+#define WORKLOAD_TYPE_WO 1
+#define WORKLOAD_TYPE_RW 2
+
+#define MAX_THREADS 1000
+#define MAX_FILENAME 64
+#define MAX_ITERATIONS 10000
+#define LATENCY_BIN_SIZE 1000
+#define LATENCY_BINS 31
+#define LOW_LATENCY_BIN_SIZE 50
+#define LOW_LATENCY_BINS 21
+#define THROUGHPUT_INTERVAL 5000
+#define DEFAULT_FILE_SIZE (262144)
+#define BLOCKSIZE 1024
+#define MAX_CMD_SIZE 256
+#define PG_MASK ~(0xFFF)
+#define kIONVMeANS2ControllerString "AppleANS2Controller"
+#define kIONVMeANS2EmbeddedControllerString "AppleANS2NVMeController"
+#define kIONVMeControllerString "AppleNVMeController"
+
+typedef enum {
+ kDefaultDevice = 0,
+ kNVMeDevice = 1,
+ kNVMeDeviceANS2 = 2,
+} qos_device_type_t;
+
+int burst_count = 10; /* Unit: Number ; Desc.: I/O Burst Count */
+int inter_burst_duration = 0; /* Unit: msecs ; Desc.: I/O Inter-Burst Duration (-1: Random value [0,100]) */
+int inter_io_delay_ms = 0; /* Unit: msecs ; Desc.: Inter I/O Delay */
+int thread_count = 1; /* Unit: Number ; Desc.: Thread Count */
+int workload_type = WORKLOAD_TYPE_RO; /* Unit: 0/1/2 ; Desc.: Workload Type */
+int io_size = 4096; /* Unit: Bytes ; Desc.: I/O Unit Size */
+int sync_frequency_ms = 0; /* Unit: msecs ; Desc.: Sync thread frequency (0: Indicates no sync) */
+int io_mode = 0; /* Unit: 0/1 ; Desc.: I/O Mode (Seq./Rand.) */
+int test_duration = 0; /* Unit: secs ; Desc.: Total Test Duration (0 indicates wait for Ctrl+C signal) */
+int io_tier = 0; /* Unit: 0/1/2/3; Desc.: I/O Tier */
+int file_size = DEFAULT_FILE_SIZE; /* Unit: pages ; Desc.: File Size in 4096 byte blocks */
+int cached_io_flag = 0; /* Unit: 0/1 ; Desc.: I/O Caching behavior (no-cached/cached) */
+int io_qos_timeout_ms = 0; /* Unit: msecs ; Desc.: I/O QOS timeout */
+char *user_fname;
+int user_specified_file = 0;
+qos_device_type_t qos_device = 0;
+
+int64_t total_io_count = 0;
+int64_t total_io_size = 0;
+int64_t total_io_time = 0;
+int64_t max_io_time = 0;
+int64_t total_burst_count = 0;
+int64_t latency_histogram[LATENCY_BINS];
+int64_t burst_latency_histogram[LATENCY_BINS];
+int64_t low_latency_histogram[LOW_LATENCY_BINS];
+int64_t throughput_histogram[MAX_ITERATIONS];
+int64_t throughput_index;
+CFRunLoopTimerRef runLoopTimer = NULL;
+
+void print_usage(void);
+void print_data_percentage(double percent);
+void print_stats(void);
+unsigned int find_io_bin(int64_t latency, int latency_bin_size, int latency_bins);
+void signalHandler(int sig);
+void assertASP(CFRunLoopTimerRef timer, void *info );
+void start_qos_timer(void);
+void stop_qos_timer(void);
+void perform_io(int fd, char *buf, int size, int type);
+void *sync_routine(void *arg);
+void *calculate_throughput(void *arg);
+void *io_routine(void *arg);
+void validate_option(int value, int min, int max, char *option, char *units);
+void print_test_setup(int value, char *option, char *units, char *comment);
+void setup_process_io_policy(int io_tier);
+void setup_qos_device(void);
+void print_latency_histogram(int64_t *data, int latency_bins, int latency_bin_size, double io_count);
+int system_cmd(char *command);
+
+void
+print_usage(void)
+{
+ printf("Usage: ./iosim [options]\n");
+ printf("Options:\n");
+ printf("-c: (number) Burst Count. No. of I/Os performed in an I/O burst\n");
+ printf("-i: (msecs) Inter Burst Duration. Amount of time the thread sleeps between bursts (-1 indicates random durations between 0-100 msecs)\n");
+ printf("-d: (msecs) Inter I/O delay. Amount of time between issuing I/Os\n");
+ printf("-t: (number) Thread count\n");
+ printf("-f: (0/1/2 : Read-Only/Write-Only/Mixed RW) Workload Type\n");
+ printf("-m: (0/1 : Sequential/Random) I/O pattern\n");
+ printf("-j: (number) Size of I/O in bytes\n");
+ printf("-s: (msecs) Frequency of sync() calls\n");
+ printf("-x: (secs) Test duration (0 indicates that the tool would wait for a Ctrl-C)\n");
+ printf("-l: (0/1/2/3) I/O Tier\n");
+ printf("-z: (number) File Size in pages (1 page = 4096 bytes) \n");
+ printf("-n: (string) File name used for tests (the tool would create files if this option is not specified)\n");
+ printf("-a: (0/1 : Non-cached/Cached) I/O Caching behavior\n");
+ printf("-q: (msecs) I/O QoS timeout. Time of I/O before drive assert and system panic\n");
+}
+
+void print_data_percentage(double percent)
+{
+ int count = (int)(round(percent / 5.0));
+ int spaces = 20 - count;
+ printf("| ");
+ for(; count > 0; count--)
+ printf("*");
+ for(; spaces > 0; spaces--)
+ printf(" ");
+ printf("|");
+}
+
+void print_latency_histogram(int64_t *data, int latency_bins, int latency_bin_size, double io_count)
+{
+ double percentage;
+ char label[MAX_FILENAME];
+ int i;
+
+ for (i = 0; i < latency_bins; i++) {
+ if (i == (latency_bins - 1))
+ snprintf(label, MAX_FILENAME, "> %d usecs", i * latency_bin_size);
+ else
+ snprintf(label, MAX_FILENAME, "%d - %d usecs", i * latency_bin_size, (i+1) * latency_bin_size);
+ printf("%25s ", label);
+ percentage = ((double)data[i] * 100.000000) / io_count;
+ print_data_percentage(percentage);
+ printf(" %.6lf%%\n", percentage);
+ }
+ printf("\n");
+}
+
+void print_stats()
+{
+ int i;
+ double percentage;
+ char label[MAX_FILENAME];
+
+ printf("I/O Statistics:\n");
+
+ printf("Total I/Os : %lld\n", total_io_count);
+ printf("Avg. Latency : %.2lf usecs\n", ((double)total_io_time) / ((double)total_io_count));
+ printf("Max. Latency : %.2lf usecs\n", ((double)max_io_time));
+
+ printf("Low Latency Histogram: \n");
+ print_latency_histogram(low_latency_histogram, LOW_LATENCY_BINS, LOW_LATENCY_BIN_SIZE, (double)total_io_count);
+ printf("Latency Histogram: \n");
+ print_latency_histogram(latency_histogram, LATENCY_BINS, LATENCY_BIN_SIZE, (double)total_io_count);
+ printf("Burst Avg. Latency Histogram: \n");
+ print_latency_histogram(burst_latency_histogram, LATENCY_BINS, LATENCY_BIN_SIZE, (double)total_burst_count);
+
+ printf("Throughput Timeline: \n");
+
+ int64_t max_throughput = 0;
+ for (i = 0; i < throughput_index; i++) {
+ if (max_throughput < throughput_histogram[i])
+ max_throughput = throughput_histogram[i];
+ }
+
+ for (i = 0; i < throughput_index; i++) {
+ snprintf(label, MAX_FILENAME, "T=%d msecs", (i+1) * THROUGHPUT_INTERVAL);
+ printf("%25s ", label);
+ percentage = ((double)throughput_histogram[i] * 100) / (double)max_throughput;
+ print_data_percentage((int)percentage);
+ printf("%.2lf MBps\n", ((double)throughput_histogram[i] / 1048576.0) / ((double)THROUGHPUT_INTERVAL / 1000.0));
+ }
+ printf("\n");
+}
+
+unsigned int find_io_bin(int64_t latency, int latency_bin_size, int latency_bins)
+{
+ int bin = (int) (latency / latency_bin_size);
+ if (bin >= latency_bins)
+ bin = latency_bins - 1;
+ return bin;
+}
+
+void signalHandler(int sig)
+{
+ printf("\n");
+ print_stats();
+ exit(0);
+}
+
+void setup_qos_device(void)
+{
+ kern_return_t status = kIOReturnError;
+ io_iterator_t iterator = IO_OBJECT_NULL;
+
+ if(io_qos_timeout_ms <= 0)
+ return;
+
+ printf ( "*** setup_qos_device *** \n" );
+
+ status = IOServiceGetMatchingServices ( kIOMasterPortDefault, IOServiceMatching ( kIONVMeANS2ControllerString ), &iterator );
+
+ if ( status != kIOReturnSuccess )
+ return;
+
+ if ( iterator != IO_OBJECT_NULL ) {
+ printf ( "Found NVMe ANS2 Device \n" );
+ qos_device = kNVMeDeviceANS2;
+ return;
+ }
+
+ status = IOServiceGetMatchingServices ( kIOMasterPortDefault, IOServiceMatching ( kIONVMeANS2EmbeddedControllerString ), &iterator );
+
+ if ( status != kIOReturnSuccess )
+ return;
+
+ if ( iterator != IO_OBJECT_NULL ) {
+ printf ( "Found NVMe ANS2 Embedded Device \n" );
+ qos_device = kNVMeDeviceANS2;
+ return;
+ }
+
+ status= IOServiceGetMatchingServices ( kIOMasterPortDefault, IOServiceMatching ( kIONVMeControllerString ), &iterator );
+
+ if ( status != kIOReturnSuccess )
+ return;
+
+ if ( iterator != IO_OBJECT_NULL ) {
+ printf ( "Found NVMe Device \n" );
+ qos_device = kNVMeDevice;
+ return;
+ }
+
+ printf ( "NVMe Device not found, not setting qos timeout\n" );
+ qos_device = kDefaultDevice;
+ return;
+}
+
+void assertASP(CFRunLoopTimerRef timer, void *info )
+{
+ char command [ 1024 ];
+
+ if(qos_device == kDefaultDevice)
+ return;
+
+ printf("assertASP. Timeout of IO exceeds = %d msec\n", io_qos_timeout_ms);
+
+ // kNVMe_ANS2_Force_Assert_offset = 0x13EC, // GP59
+ // kNVMe_Force_Assert_Offset = 0x550,
+
+ if(qos_device == kNVMeDeviceANS2)
+ snprintf ( command, sizeof ( command ), "/usr/local/bin/nvmectl-tool.py -a WriteRegister32 $((0x13EC)) 0xFFFF" );
+ else if(qos_device == kNVMeDevice)
+ snprintf ( command, sizeof ( command ), "/usr/local/bin/nvmectl-tool.py -a WriteRegister32 $((0x550)) 0xFFFF" );
+ else
+ return;
+
+ // Assert ASP
+ printf("Command : %s\n", command);
+ system_cmd(command);
+
+ // Panic the system as well
+ panic("IO time > QoS timeout");
+
+ return;
+}
+
+void start_qos_timer(void)
+{
+ float timeout_sec;
+
+ if(io_qos_timeout_ms <= 0)
+ return;
+
+ timeout_sec = (float)io_qos_timeout_ms/1000;
+
+ // Schedule a "timeout" delayed task that checks IO's which take > timeout sec to complete
+ runLoopTimer = CFRunLoopTimerCreate(NULL, CFAbsoluteTimeGetCurrent()+timeout_sec, 0, 0, 0, assertASP, NULL);
+ CFRunLoopAddTimer(CFRunLoopGetMain(), runLoopTimer, kCFRunLoopDefaultMode);
+}
+
+void stop_qos_timer(void)
+{
+ if(runLoopTimer == NULL)
+ return;
+
+ CFRunLoopTimerInvalidate(runLoopTimer);
+ CFRunLoopRemoveTimer(CFRunLoopGetMain(), runLoopTimer, kCFRunLoopDefaultMode);
+ CFRelease(runLoopTimer);
+}
+
+void perform_io(int fd, char *buf, int size, int type)
+{
+ long ret;
+
+ if (type == WORKLOAD_TYPE_RW)
+ type = (rand() % 2) ? WORKLOAD_TYPE_WO : WORKLOAD_TYPE_RO;
+
+ while(size > 0) {
+
+ if (type == WORKLOAD_TYPE_RO)
+ ret = read(fd, buf, size);
+ else
+ ret = write(fd, buf, size);
+
+ if (ret == 0) {
+ if (lseek(fd, 0, SEEK_SET) < 0) {
+ perror("lseek() to reset file offset to zero failed!\n");
+ goto error;
+ }
+ }
+
+ if (ret < 0) {
+ perror("read/write syscall failed!\n");
+ goto error;
+ }
+ size -= ret;
+ }
+
+ return;
+
+error:
+ print_stats();
+ exit(1);
+}
+
+void *sync_routine(void *arg)
+{
+ while(1) {
+ usleep(sync_frequency_ms * 1000);
+ sync();
+ }
+ pthread_exit(NULL);
+}
+
+void *calculate_throughput(void *arg)
+{
+ int64_t prev_total_io_size = 0;
+ int64_t size;
+
+ while(1) {
+ usleep(THROUGHPUT_INTERVAL * 1000);
+ size = total_io_size - prev_total_io_size;
+ throughput_histogram[throughput_index] = size;
+ prev_total_io_size = total_io_size;
+ throughput_index++;
+ }
+ pthread_exit(NULL);
+}
+
+void *io_routine(void *arg)
+{
+ struct timeval start_tv;
+ struct timeval end_tv;
+ int64_t elapsed;
+ int64_t burst_elapsed;
+ char *data;
+ char test_filename[MAX_FILENAME];
+ struct stat filestat;
+ int i, fd, io_thread_id;
+
+ io_thread_id = (int)arg;
+ if (user_specified_file)
+ strlcpy(test_filename, user_fname, MAX_FILENAME);
+ else
+ snprintf(test_filename, MAX_FILENAME, "iosim-%d-%d", (int)getpid(), io_thread_id);
+
+ if (0 > (fd = open(test_filename, O_RDWR, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH))) {
+ printf("Error opening file %s!\n", test_filename);
+ exit(1);
+ }
+
+ if (fstat(fd, &filestat) < 0) {
+ printf("Error stat()ing file %s!\n", test_filename);
+ exit(1);
+ }
+
+ if (filestat.st_size < io_size) {
+ printf("%s: File size (%lld) smaller than I/O size (%d)!\n", test_filename, filestat.st_size, io_size);
+ exit(1);
+ }
+
+ if (!cached_io_flag)
+ fcntl(fd, F_NOCACHE, 1);
+
+ fcntl(fd, F_RDAHEAD, 0);
+
+ if(!(data = (char *)calloc(io_size, 1))) {
+ perror("Error allocating buffers for I/O!\n");
+ exit(1);
+ }
+ memset(data, '\0', io_size);
+
+ while(1) {
+ burst_elapsed = 0;
+
+ for(i = 0; i < burst_count; i++) {
+ if (io_mode == IO_MODE_RANDOM) {
+ if (lseek(fd, (rand() % (filestat.st_size - io_size)) & PG_MASK, SEEK_SET) < 0) {
+ perror("Error lseek()ing to random location in file!\n");
+ exit(1);
+ }
+ }
+
+ start_qos_timer();
+
+ gettimeofday(&start_tv, NULL);
+ perform_io(fd, data, io_size, workload_type);
+ gettimeofday(&end_tv, NULL);
+
+ stop_qos_timer();
+
+ OSAtomicIncrement64(&total_io_count);
+ OSAtomicAdd64(io_size, &total_io_size);
+ elapsed = ((end_tv.tv_sec - start_tv.tv_sec) * 1000000) + (end_tv.tv_usec - start_tv.tv_usec);
+
+ if (elapsed > max_io_time) {
+ max_io_time = elapsed;
+ }
+
+ OSAtomicAdd64(elapsed, &total_io_time);
+ OSAtomicIncrement64(&(latency_histogram[find_io_bin(elapsed, LATENCY_BIN_SIZE, LATENCY_BINS)]));
+ OSAtomicIncrement64(&(low_latency_histogram[find_io_bin(elapsed, LOW_LATENCY_BIN_SIZE, LOW_LATENCY_BINS)]));
+ burst_elapsed += elapsed;
+
+ if (inter_io_delay_ms)
+ usleep(inter_io_delay_ms * 1000);
+ }
+
+ burst_elapsed /= burst_count;
+ OSAtomicIncrement64(&(burst_latency_histogram[find_io_bin(burst_elapsed, LATENCY_BIN_SIZE, LATENCY_BINS)]));
+ OSAtomicIncrement64(&total_burst_count);
+
+ if(inter_burst_duration == -1)
+ usleep((rand() % 100) * 1000);
+ else
+ usleep(inter_burst_duration * 1000);
+ }
+
+ free(data);
+ close(fd);
+ pthread_exit(NULL);
+}
+
+void validate_option(int value, int min, int max, char *option, char *units)
+{
+ if (value < min || value > max) {
+ printf("Illegal option value %d for %s (Min value: %d %s, Max value: %d %s).\n", value, option, min, units, max, units);
+ exit(1);
+ }
+}
+
+void print_test_setup(int value, char *option, char *units, char *comment)
+{
+ if (comment == NULL)
+ printf("%32s: %16d %-16s\n", option, value, units);
+ else
+ printf("%32s: %16d %-16s (%s)\n", option, value, units, comment);
+}
+
+void setup_process_io_policy(int io_tier)
+{
+ switch(io_tier)
+ {
+ case 0:
+ if (setiopolicy_np(IOPOL_TYPE_DISK, IOPOL_SCOPE_PROCESS, IOPOL_IMPORTANT))
+ goto iopol_error;
+ break;
+ case 1:
+ if (setiopolicy_np(IOPOL_TYPE_DISK, IOPOL_SCOPE_PROCESS, IOPOL_STANDARD))
+ goto iopol_error;
+ break;
+ case 2:
+ if (setiopolicy_np(IOPOL_TYPE_DISK, IOPOL_SCOPE_PROCESS, IOPOL_UTILITY))
+ goto iopol_error;
+ break;
+ case 3:
+ if (setiopolicy_np(IOPOL_TYPE_DISK, IOPOL_SCOPE_PROCESS, IOPOL_THROTTLE))
+ goto iopol_error;
+ break;
+ }
+ return;
+
+iopol_error:
+ printf("Error setting process-wide I/O policy to %d\n", io_tier);
+ exit(1);
+}
+
+int main(int argc, char *argv[])
+{
+ int i, option = 0;
+ pthread_t thread_list[MAX_THREADS];
+ pthread_t sync_thread;
+ pthread_t throughput_thread;
+ char fname[MAX_FILENAME];
+
+ while((option = getopt(argc, argv,"hc:i:d:t:f:m:j:s:x:l:z:n:a:q:")) != -1) {
+ switch(option) {
+ case 'c':
+ burst_count = atoi(optarg);
+ validate_option(burst_count, 0, INT_MAX, "Burst Count", "I/Os");
+ break;
+ case 'i':
+ inter_burst_duration = atoi(optarg);
+ validate_option(inter_burst_duration, -1, INT_MAX, "Inter Burst duration", "msecs");
+ break;
+ case 'd':
+ inter_io_delay_ms = atoi(optarg);
+ validate_option(inter_io_delay_ms, 0, INT_MAX, "Inter I/O Delay", "msecs");
+ break;
+ case 't':
+ thread_count = atoi(optarg);
+ validate_option(thread_count, 0, MAX_THREADS, "Thread Count", "Threads");
+ break;
+ case 'f':
+ workload_type = atoi(optarg);
+ validate_option(workload_type, 0, 2, "Workload Type", "");
+ break;
+ case 'm':
+ io_mode = atoi(optarg);
+ validate_option(io_mode, 0, 1, "I/O Mode", "");
+ break;
+ case 'j':
+ io_size = atoi(optarg);
+ validate_option(io_size, 0, INT_MAX, "I/O Size", "Bytes");
+ break;
+ case 'h':
+ print_usage();
+ exit(1);
+ case 's':
+ sync_frequency_ms = atoi(optarg);
+ validate_option(sync_frequency_ms, 0, INT_MAX, "Sync. Frequency", "msecs");
+ break;
+ case 'x':
+ test_duration = atoi(optarg);
+ validate_option(test_duration, 0, INT_MAX, "Test duration", "secs");
+ break;
+ case 'l':
+ io_tier = atoi(optarg);
+ validate_option(io_tier, 0, 3, "I/O Tier", "");
+ break;
+ case 'z':
+ file_size = atoi(optarg);
+ validate_option(file_size, 0, INT_MAX, "File Size", "bytes");
+ break;
+ case 'n':
+ user_fname = optarg;
+ user_specified_file = 1;
+ break;
+ case 'a':
+ cached_io_flag = atoi(optarg);
+ validate_option(cached_io_flag, 0, 1, "I/Os cached/no-cached", "");
+ break;
+ case 'q':
+ io_qos_timeout_ms = atoi(optarg);
+ validate_option(io_qos_timeout_ms, 0, INT_MAX, "I/O QoS timeout", "msecs");
+ break;
+ default:
+ printf("Unknown option %c\n", option);
+ print_usage();
+ exit(1);
+ }
+ }
+
+ printf("***********************TEST SETUP*************************\n");
+
+ print_test_setup(burst_count, "Burst Count", "I/Os", 0);
+ print_test_setup(inter_burst_duration, "Inter Burst duration", "msecs", "-1 indicates random burst duration");
+ print_test_setup(inter_io_delay_ms, "Inter I/O Delay", "msecs", 0);
+ print_test_setup(thread_count, "Thread Count", "Threads", 0);
+ print_test_setup(workload_type, "Workload Type", "", "0:R 1:W 2:RW");
+ print_test_setup(io_mode, "I/O Mode", "", "0:Seq. 1:Rnd");
+ print_test_setup(io_size, "I/O Size", "Bytes", 0);
+ print_test_setup(sync_frequency_ms, "Sync. Frequency", "msecs", "0 indicates no sync. thread");
+ print_test_setup(test_duration, "Test duration", "secs", "0 indicates tool waits for Ctrl+C");
+ print_test_setup(io_tier, "I/O Tier", "", 0);
+ print_test_setup(cached_io_flag, "I/O Caching", "", "0 indicates non-cached I/Os");
+ print_test_setup(io_qos_timeout_ms, "I/O QoS Threshold Timeout", "msecs", 0);
+ print_test_setup(0, "File read-aheads", "", "0 indicates read-aheads disabled");
+
+ printf("**********************************************************\n");
+
+ if (user_specified_file == 0) {
+ char dd_command[MAX_CMD_SIZE];
+ for (i=0; i < thread_count; i++) {
+ snprintf(fname, MAX_FILENAME, "iosim-%d-%d", (int)getpid(), i);
+ snprintf(dd_command, MAX_CMD_SIZE, "dd if=/dev/urandom of=%s bs=4096 count=%d", fname, file_size);
+ printf("Creating file %s of size %lld...\n", fname, ((int64_t)file_size * 4096));
+ system_cmd(dd_command);
+ }
+ } else {
+ printf("Using user specified file %s for all threads...\n", user_fname);
+ }
+ system_cmd("purge");
+ setup_process_io_policy(io_tier);
+
+ setup_qos_device();
+
+ printf("**********************************************************\n");
+ printf("Creating threads and generating workload...\n");
+
+ signal(SIGINT, signalHandler);
+ signal(SIGALRM, signalHandler);
+
+ for(i=0; i < thread_count; i++) {
+ if (pthread_create(&thread_list[i], NULL, io_routine, i) < 0) {
+ perror("Could not create I/O thread!\n");
+ exit(1);
+ }
+ }
+
+ if (sync_frequency_ms) {
+ if (pthread_create(&sync_thread, NULL, sync_routine, NULL) < 0) {
+ perror("Could not create sync thread!\n");
+ exit(1);
+ }
+ }
+
+ if (pthread_create(&throughput_thread, NULL, calculate_throughput, NULL) < 0) {
+ perror("Could not throughput calculation thread!\n");
+ exit(1);
+ }
+
+ if(io_qos_timeout_ms > 0) {
+ CFRunLoopRunInMode(kCFRunLoopDefaultMode, (CFTimeInterval)test_duration, false);
+ alarm(1);
+ } else {
+ /* All threads are now initialized */
+ if (test_duration)
+ alarm(test_duration);
+ }
+
+ for(i=0; i < thread_count; i++)
+ pthread_join(thread_list[i], NULL);
+
+ if (sync_frequency_ms)
+ pthread_join(sync_thread, NULL);
+
+ pthread_join(throughput_thread, NULL);
+
+ pthread_exit(0);
+}
+
+extern char **environ;
+
+int system_cmd(char *command)
+{
+ // workaround for rdar://problem/53281655
+ pid_t pid;
+ char *argv[] = {"sh", "-c", command, NULL};
+ int status;
+ status = posix_spawn(&pid, "/bin/sh", NULL, NULL, argv, environ);
+ if (status == 0) {
+ if (waitpid(pid, &status, 0) != -1) {
+ return status;
+ } else {
+ perror("waitpid");
+ }
+ }
+ return -1;
+}