X-Git-Url: https://git.saurik.com/apple/system_cmds.git/blobdiff_plain/1a7e3f61d38d679bba59130891c2031b5a0092b6..HEAD:/iosim.tproj/iosim.c?ds=sidebyside diff --git a/iosim.tproj/iosim.c b/iosim.tproj/iosim.c index 349b9c8..7a0efce 100644 --- a/iosim.tproj/iosim.c +++ b/iosim.tproj/iosim.c @@ -14,57 +14,77 @@ #include #include #include - -#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 500 -#define LATENCY_BINS 11 -#define LOW_LATENCY_BIN_SIZE 50 -#define LOW_LATENCY_BINS 11 +#include +#include "panic.h" +#include +#include + +#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) - -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 */ +#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 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) */ -char *user_fname; +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; -int64_t total_io_size; -int64_t total_io_time; -int64_t total_burst_count; +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(int percent); +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); @@ -72,9 +92,12 @@ 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 print_latency_histogram(int64_t *data, int latency_bins, int latency_bin_size); +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 +print_usage(void) { printf("Usage: ./iosim [options]\n"); printf("Options:\n"); @@ -91,9 +114,10 @@ void print_usage() 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(int percent) +void print_data_percentage(double percent) { int count = (int)(round(percent / 5.0)); int spaces = 20 - count; @@ -105,7 +129,7 @@ void print_data_percentage(int percent) printf("|"); } -void print_latency_histogram(int64_t *data, int latency_bins, int latency_bin_size) +void print_latency_histogram(int64_t *data, int latency_bins, int latency_bin_size, double io_count) { double percentage; char label[MAX_FILENAME]; @@ -117,9 +141,9 @@ void print_latency_histogram(int64_t *data, int latency_bins, int latency_bin_si 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.0) / (double)total_io_count; - print_data_percentage((int)percentage); - printf(" %.2lf%%\n", percentage); + percentage = ((double)data[i] * 100.000000) / io_count; + print_data_percentage(percentage); + printf(" %.6lf%%\n", percentage); } printf("\n"); } @@ -133,15 +157,16 @@ void print_stats() 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("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); + 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); + 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); - + print_latency_histogram(burst_latency_histogram, LATENCY_BINS, LATENCY_BIN_SIZE, (double)total_burst_count); + printf("Throughput Timeline: \n"); int64_t max_throughput = 0; @@ -158,7 +183,6 @@ void print_stats() 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) @@ -176,6 +200,106 @@ void signalHandler(int sig) 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) { @@ -190,14 +314,14 @@ void perform_io(int fd, char *buf, int size, int type) 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; @@ -214,7 +338,7 @@ error: void *sync_routine(void *arg) { - while(1) { + while(1) { usleep(sync_frequency_ms * 1000); sync(); } @@ -231,10 +355,10 @@ void *calculate_throughput(void *arg) size = total_io_size - prev_total_io_size; throughput_histogram[throughput_index] = size; prev_total_io_size = total_io_size; - throughput_index++; + throughput_index++; } pthread_exit(NULL); -} +} void *io_routine(void *arg) { @@ -249,7 +373,7 @@ void *io_routine(void *arg) io_thread_id = (int)arg; if (user_specified_file) - strncpy(test_filename, user_fname, MAX_FILENAME); + strlcpy(test_filename, user_fname, MAX_FILENAME); else snprintf(test_filename, MAX_FILENAME, "iosim-%d-%d", (int)getpid(), io_thread_id); @@ -261,7 +385,7 @@ void *io_routine(void *arg) 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); @@ -272,7 +396,7 @@ void *io_routine(void *arg) 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); @@ -280,7 +404,6 @@ void *io_routine(void *arg) memset(data, '\0', io_size); while(1) { - burst_elapsed = 0; for(i = 0; i < burst_count; i++) { @@ -290,20 +413,28 @@ void *io_routine(void *arg) 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); } @@ -375,7 +506,7 @@ int main(int argc, char *argv[]) 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:")) != -1) { + 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); @@ -423,7 +554,7 @@ int main(int argc, char *argv[]) case 'z': file_size = atoi(optarg); validate_option(file_size, 0, INT_MAX, "File Size", "bytes"); - break; + break; case 'n': user_fname = optarg; user_specified_file = 1; @@ -432,6 +563,10 @@ int main(int argc, char *argv[]) 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(); @@ -452,26 +587,29 @@ int main(int argc, char *argv[]) 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); + 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(dd_command); + system_cmd(dd_command); } } else { printf("Using user specified file %s for all threads...\n", user_fname); } - system("purge"); + system_cmd("purge"); setup_process_io_policy(io_tier); - printf("**********************************************************\n"); - printf("Creating threads and generating workload...\n"); + setup_qos_device(); + + printf("**********************************************************\n"); + printf("Creating threads and generating workload...\n"); signal(SIGINT, signalHandler); signal(SIGALRM, signalHandler); @@ -495,18 +633,41 @@ int main(int argc, char *argv[]) exit(1); } - /* All threads are now initialized */ - if (test_duration) - alarm(test_duration); + 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; }