]> git.cameronkatri.com Git - apple_cmds.git/blob - system_cmds/iosim.tproj/iosim.c
remote_cmds: All compiling
[apple_cmds.git] / system_cmds / iosim.tproj / iosim.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <fcntl.h>
4 #include <unistd.h>
5 #include <getopt.h>
6 #include <string.h>
7 #include <sys/resource.h>
8 #include <pthread.h>
9 #include <sys/time.h>
10 #include <sys/stat.h>
11 #include <sys/types.h>
12 #include <math.h>
13 #include <signal.h>
14 #include <libkern/OSAtomic.h>
15 #include <limits.h>
16 #include <errno.h>
17 #include <CoreFoundation/CoreFoundation.h>
18 #include "panic.h"
19 #include <IOKit/IOKitLib.h>
20 #include <spawn.h>
21
22 #define IO_MODE_SEQ 0
23 #define IO_MODE_RANDOM 1
24
25 #define WORKLOAD_TYPE_RO 0
26 #define WORKLOAD_TYPE_WO 1
27 #define WORKLOAD_TYPE_RW 2
28
29 #define MAX_THREADS 1000
30 #define MAX_FILENAME 64
31 #define MAX_ITERATIONS 10000
32 #define LATENCY_BIN_SIZE 1000
33 #define LATENCY_BINS 31
34 #define LOW_LATENCY_BIN_SIZE 50
35 #define LOW_LATENCY_BINS 21
36 #define THROUGHPUT_INTERVAL 5000
37 #define DEFAULT_FILE_SIZE (262144)
38 #define BLOCKSIZE 1024
39 #define MAX_CMD_SIZE 256
40 #define PG_MASK ~(0xFFF)
41 #define kIONVMeANS2ControllerString "AppleANS2Controller"
42 #define kIONVMeANS2EmbeddedControllerString "AppleANS2NVMeController"
43 #define kIONVMeControllerString "AppleNVMeController"
44
45 typedef enum {
46 kDefaultDevice = 0,
47 kNVMeDevice = 1,
48 kNVMeDeviceANS2 = 2,
49 } qos_device_type_t;
50
51 int burst_count = 10; /* Unit: Number ; Desc.: I/O Burst Count */
52 int inter_burst_duration = 0; /* Unit: msecs ; Desc.: I/O Inter-Burst Duration (-1: Random value [0,100]) */
53 int inter_io_delay_ms = 0; /* Unit: msecs ; Desc.: Inter I/O Delay */
54 int thread_count = 1; /* Unit: Number ; Desc.: Thread Count */
55 int workload_type = WORKLOAD_TYPE_RO; /* Unit: 0/1/2 ; Desc.: Workload Type */
56 int io_size = 4096; /* Unit: Bytes ; Desc.: I/O Unit Size */
57 int sync_frequency_ms = 0; /* Unit: msecs ; Desc.: Sync thread frequency (0: Indicates no sync) */
58 int io_mode = 0; /* Unit: 0/1 ; Desc.: I/O Mode (Seq./Rand.) */
59 int test_duration = 0; /* Unit: secs ; Desc.: Total Test Duration (0 indicates wait for Ctrl+C signal) */
60 int io_tier = 0; /* Unit: 0/1/2/3; Desc.: I/O Tier */
61 int file_size = DEFAULT_FILE_SIZE; /* Unit: pages ; Desc.: File Size in 4096 byte blocks */
62 int cached_io_flag = 0; /* Unit: 0/1 ; Desc.: I/O Caching behavior (no-cached/cached) */
63 int io_qos_timeout_ms = 0; /* Unit: msecs ; Desc.: I/O QOS timeout */
64 char *user_fname;
65 int user_specified_file = 0;
66 qos_device_type_t qos_device = 0;
67
68 int64_t total_io_count = 0;
69 int64_t total_io_size = 0;
70 int64_t total_io_time = 0;
71 int64_t max_io_time = 0;
72 int64_t total_burst_count = 0;
73 int64_t latency_histogram[LATENCY_BINS];
74 int64_t burst_latency_histogram[LATENCY_BINS];
75 int64_t low_latency_histogram[LOW_LATENCY_BINS];
76 int64_t throughput_histogram[MAX_ITERATIONS];
77 int64_t throughput_index;
78 CFRunLoopTimerRef runLoopTimer = NULL;
79
80 void print_usage(void);
81 void print_data_percentage(double percent);
82 void print_stats(void);
83 unsigned int find_io_bin(int64_t latency, int latency_bin_size, int latency_bins);
84 void signalHandler(int sig);
85 void assertASP(CFRunLoopTimerRef timer, void *info );
86 void start_qos_timer(void);
87 void stop_qos_timer(void);
88 void perform_io(int fd, char *buf, int size, int type);
89 void *sync_routine(void *arg);
90 void *calculate_throughput(void *arg);
91 void *io_routine(void *arg);
92 void validate_option(int value, int min, int max, char *option, char *units);
93 void print_test_setup(int value, char *option, char *units, char *comment);
94 void setup_process_io_policy(int io_tier);
95 void setup_qos_device(void);
96 void print_latency_histogram(int64_t *data, int latency_bins, int latency_bin_size, double io_count);
97 int system_cmd(char *command);
98
99 void
100 print_usage(void)
101 {
102 printf("Usage: ./iosim [options]\n");
103 printf("Options:\n");
104 printf("-c: (number) Burst Count. No. of I/Os performed in an I/O burst\n");
105 printf("-i: (msecs) Inter Burst Duration. Amount of time the thread sleeps between bursts (-1 indicates random durations between 0-100 msecs)\n");
106 printf("-d: (msecs) Inter I/O delay. Amount of time between issuing I/Os\n");
107 printf("-t: (number) Thread count\n");
108 printf("-f: (0/1/2 : Read-Only/Write-Only/Mixed RW) Workload Type\n");
109 printf("-m: (0/1 : Sequential/Random) I/O pattern\n");
110 printf("-j: (number) Size of I/O in bytes\n");
111 printf("-s: (msecs) Frequency of sync() calls\n");
112 printf("-x: (secs) Test duration (0 indicates that the tool would wait for a Ctrl-C)\n");
113 printf("-l: (0/1/2/3) I/O Tier\n");
114 printf("-z: (number) File Size in pages (1 page = 4096 bytes) \n");
115 printf("-n: (string) File name used for tests (the tool would create files if this option is not specified)\n");
116 printf("-a: (0/1 : Non-cached/Cached) I/O Caching behavior\n");
117 printf("-q: (msecs) I/O QoS timeout. Time of I/O before drive assert and system panic\n");
118 }
119
120 void print_data_percentage(double percent)
121 {
122 int count = (int)(round(percent / 5.0));
123 int spaces = 20 - count;
124 printf("| ");
125 for(; count > 0; count--)
126 printf("*");
127 for(; spaces > 0; spaces--)
128 printf(" ");
129 printf("|");
130 }
131
132 void print_latency_histogram(int64_t *data, int latency_bins, int latency_bin_size, double io_count)
133 {
134 double percentage;
135 char label[MAX_FILENAME];
136 int i;
137
138 for (i = 0; i < latency_bins; i++) {
139 if (i == (latency_bins - 1))
140 snprintf(label, MAX_FILENAME, "> %d usecs", i * latency_bin_size);
141 else
142 snprintf(label, MAX_FILENAME, "%d - %d usecs", i * latency_bin_size, (i+1) * latency_bin_size);
143 printf("%25s ", label);
144 percentage = ((double)data[i] * 100.000000) / io_count;
145 print_data_percentage(percentage);
146 printf(" %.6lf%%\n", percentage);
147 }
148 printf("\n");
149 }
150
151 void print_stats()
152 {
153 int i;
154 double percentage;
155 char label[MAX_FILENAME];
156
157 printf("I/O Statistics:\n");
158
159 printf("Total I/Os : %lld\n", total_io_count);
160 printf("Avg. Latency : %.2lf usecs\n", ((double)total_io_time) / ((double)total_io_count));
161 printf("Max. Latency : %.2lf usecs\n", ((double)max_io_time));
162
163 printf("Low Latency Histogram: \n");
164 print_latency_histogram(low_latency_histogram, LOW_LATENCY_BINS, LOW_LATENCY_BIN_SIZE, (double)total_io_count);
165 printf("Latency Histogram: \n");
166 print_latency_histogram(latency_histogram, LATENCY_BINS, LATENCY_BIN_SIZE, (double)total_io_count);
167 printf("Burst Avg. Latency Histogram: \n");
168 print_latency_histogram(burst_latency_histogram, LATENCY_BINS, LATENCY_BIN_SIZE, (double)total_burst_count);
169
170 printf("Throughput Timeline: \n");
171
172 int64_t max_throughput = 0;
173 for (i = 0; i < throughput_index; i++) {
174 if (max_throughput < throughput_histogram[i])
175 max_throughput = throughput_histogram[i];
176 }
177
178 for (i = 0; i < throughput_index; i++) {
179 snprintf(label, MAX_FILENAME, "T=%d msecs", (i+1) * THROUGHPUT_INTERVAL);
180 printf("%25s ", label);
181 percentage = ((double)throughput_histogram[i] * 100) / (double)max_throughput;
182 print_data_percentage((int)percentage);
183 printf("%.2lf MBps\n", ((double)throughput_histogram[i] / 1048576.0) / ((double)THROUGHPUT_INTERVAL / 1000.0));
184 }
185 printf("\n");
186 }
187
188 unsigned int find_io_bin(int64_t latency, int latency_bin_size, int latency_bins)
189 {
190 int bin = (int) (latency / latency_bin_size);
191 if (bin >= latency_bins)
192 bin = latency_bins - 1;
193 return bin;
194 }
195
196 void signalHandler(int sig)
197 {
198 printf("\n");
199 print_stats();
200 exit(0);
201 }
202
203 void setup_qos_device(void)
204 {
205 kern_return_t status = kIOReturnError;
206 io_iterator_t iterator = IO_OBJECT_NULL;
207
208 if(io_qos_timeout_ms <= 0)
209 return;
210
211 printf ( "*** setup_qos_device *** \n" );
212
213 status = IOServiceGetMatchingServices ( kIOMasterPortDefault, IOServiceMatching ( kIONVMeANS2ControllerString ), &iterator );
214
215 if ( status != kIOReturnSuccess )
216 return;
217
218 if ( iterator != IO_OBJECT_NULL ) {
219 printf ( "Found NVMe ANS2 Device \n" );
220 qos_device = kNVMeDeviceANS2;
221 return;
222 }
223
224 status = IOServiceGetMatchingServices ( kIOMasterPortDefault, IOServiceMatching ( kIONVMeANS2EmbeddedControllerString ), &iterator );
225
226 if ( status != kIOReturnSuccess )
227 return;
228
229 if ( iterator != IO_OBJECT_NULL ) {
230 printf ( "Found NVMe ANS2 Embedded Device \n" );
231 qos_device = kNVMeDeviceANS2;
232 return;
233 }
234
235 status= IOServiceGetMatchingServices ( kIOMasterPortDefault, IOServiceMatching ( kIONVMeControllerString ), &iterator );
236
237 if ( status != kIOReturnSuccess )
238 return;
239
240 if ( iterator != IO_OBJECT_NULL ) {
241 printf ( "Found NVMe Device \n" );
242 qos_device = kNVMeDevice;
243 return;
244 }
245
246 printf ( "NVMe Device not found, not setting qos timeout\n" );
247 qos_device = kDefaultDevice;
248 return;
249 }
250
251 void assertASP(CFRunLoopTimerRef timer, void *info )
252 {
253 char command [ 1024 ];
254
255 if(qos_device == kDefaultDevice)
256 return;
257
258 printf("assertASP. Timeout of IO exceeds = %d msec\n", io_qos_timeout_ms);
259
260 // kNVMe_ANS2_Force_Assert_offset = 0x13EC, // GP59
261 // kNVMe_Force_Assert_Offset = 0x550,
262
263 if(qos_device == kNVMeDeviceANS2)
264 snprintf ( command, sizeof ( command ), "/usr/local/bin/nvmectl-tool.py -a WriteRegister32 $((0x13EC)) 0xFFFF" );
265 else if(qos_device == kNVMeDevice)
266 snprintf ( command, sizeof ( command ), "/usr/local/bin/nvmectl-tool.py -a WriteRegister32 $((0x550)) 0xFFFF" );
267 else
268 return;
269
270 // Assert ASP
271 printf("Command : %s\n", command);
272 system_cmd(command);
273
274 // Panic the system as well
275 panic("IO time > QoS timeout");
276
277 return;
278 }
279
280 void start_qos_timer(void)
281 {
282 float timeout_sec;
283
284 if(io_qos_timeout_ms <= 0)
285 return;
286
287 timeout_sec = (float)io_qos_timeout_ms/1000;
288
289 // Schedule a "timeout" delayed task that checks IO's which take > timeout sec to complete
290 runLoopTimer = CFRunLoopTimerCreate(NULL, CFAbsoluteTimeGetCurrent()+timeout_sec, 0, 0, 0, assertASP, NULL);
291 CFRunLoopAddTimer(CFRunLoopGetMain(), runLoopTimer, kCFRunLoopDefaultMode);
292 }
293
294 void stop_qos_timer(void)
295 {
296 if(runLoopTimer == NULL)
297 return;
298
299 CFRunLoopTimerInvalidate(runLoopTimer);
300 CFRunLoopRemoveTimer(CFRunLoopGetMain(), runLoopTimer, kCFRunLoopDefaultMode);
301 CFRelease(runLoopTimer);
302 }
303
304 void perform_io(int fd, char *buf, int size, int type)
305 {
306 long ret;
307
308 if (type == WORKLOAD_TYPE_RW)
309 type = (rand() % 2) ? WORKLOAD_TYPE_WO : WORKLOAD_TYPE_RO;
310
311 while(size > 0) {
312
313 if (type == WORKLOAD_TYPE_RO)
314 ret = read(fd, buf, size);
315 else
316 ret = write(fd, buf, size);
317
318 if (ret == 0) {
319 if (lseek(fd, 0, SEEK_SET) < 0) {
320 perror("lseek() to reset file offset to zero failed!\n");
321 goto error;
322 }
323 }
324
325 if (ret < 0) {
326 perror("read/write syscall failed!\n");
327 goto error;
328 }
329 size -= ret;
330 }
331
332 return;
333
334 error:
335 print_stats();
336 exit(1);
337 }
338
339 void *sync_routine(void *arg)
340 {
341 while(1) {
342 usleep(sync_frequency_ms * 1000);
343 sync();
344 }
345 pthread_exit(NULL);
346 }
347
348 void *calculate_throughput(void *arg)
349 {
350 int64_t prev_total_io_size = 0;
351 int64_t size;
352
353 while(1) {
354 usleep(THROUGHPUT_INTERVAL * 1000);
355 size = total_io_size - prev_total_io_size;
356 throughput_histogram[throughput_index] = size;
357 prev_total_io_size = total_io_size;
358 throughput_index++;
359 }
360 pthread_exit(NULL);
361 }
362
363 void *io_routine(void *arg)
364 {
365 struct timeval start_tv;
366 struct timeval end_tv;
367 int64_t elapsed;
368 int64_t burst_elapsed;
369 char *data;
370 char test_filename[MAX_FILENAME];
371 struct stat filestat;
372 int i, fd, io_thread_id;
373
374 io_thread_id = (int)arg;
375 if (user_specified_file)
376 strlcpy(test_filename, user_fname, MAX_FILENAME);
377 else
378 snprintf(test_filename, MAX_FILENAME, "iosim-%d-%d", (int)getpid(), io_thread_id);
379
380 if (0 > (fd = open(test_filename, O_RDWR, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH))) {
381 printf("Error opening file %s!\n", test_filename);
382 exit(1);
383 }
384
385 if (fstat(fd, &filestat) < 0) {
386 printf("Error stat()ing file %s!\n", test_filename);
387 exit(1);
388 }
389
390 if (filestat.st_size < io_size) {
391 printf("%s: File size (%lld) smaller than I/O size (%d)!\n", test_filename, filestat.st_size, io_size);
392 exit(1);
393 }
394
395 if (!cached_io_flag)
396 fcntl(fd, F_NOCACHE, 1);
397
398 fcntl(fd, F_RDAHEAD, 0);
399
400 if(!(data = (char *)calloc(io_size, 1))) {
401 perror("Error allocating buffers for I/O!\n");
402 exit(1);
403 }
404 memset(data, '\0', io_size);
405
406 while(1) {
407 burst_elapsed = 0;
408
409 for(i = 0; i < burst_count; i++) {
410 if (io_mode == IO_MODE_RANDOM) {
411 if (lseek(fd, (rand() % (filestat.st_size - io_size)) & PG_MASK, SEEK_SET) < 0) {
412 perror("Error lseek()ing to random location in file!\n");
413 exit(1);
414 }
415 }
416
417 start_qos_timer();
418
419 gettimeofday(&start_tv, NULL);
420 perform_io(fd, data, io_size, workload_type);
421 gettimeofday(&end_tv, NULL);
422
423 stop_qos_timer();
424
425 OSAtomicIncrement64(&total_io_count);
426 OSAtomicAdd64(io_size, &total_io_size);
427 elapsed = ((end_tv.tv_sec - start_tv.tv_sec) * 1000000) + (end_tv.tv_usec - start_tv.tv_usec);
428
429 if (elapsed > max_io_time) {
430 max_io_time = elapsed;
431 }
432
433 OSAtomicAdd64(elapsed, &total_io_time);
434 OSAtomicIncrement64(&(latency_histogram[find_io_bin(elapsed, LATENCY_BIN_SIZE, LATENCY_BINS)]));
435 OSAtomicIncrement64(&(low_latency_histogram[find_io_bin(elapsed, LOW_LATENCY_BIN_SIZE, LOW_LATENCY_BINS)]));
436 burst_elapsed += elapsed;
437
438 if (inter_io_delay_ms)
439 usleep(inter_io_delay_ms * 1000);
440 }
441
442 burst_elapsed /= burst_count;
443 OSAtomicIncrement64(&(burst_latency_histogram[find_io_bin(burst_elapsed, LATENCY_BIN_SIZE, LATENCY_BINS)]));
444 OSAtomicIncrement64(&total_burst_count);
445
446 if(inter_burst_duration == -1)
447 usleep((rand() % 100) * 1000);
448 else
449 usleep(inter_burst_duration * 1000);
450 }
451
452 free(data);
453 close(fd);
454 pthread_exit(NULL);
455 }
456
457 void validate_option(int value, int min, int max, char *option, char *units)
458 {
459 if (value < min || value > max) {
460 printf("Illegal option value %d for %s (Min value: %d %s, Max value: %d %s).\n", value, option, min, units, max, units);
461 exit(1);
462 }
463 }
464
465 void print_test_setup(int value, char *option, char *units, char *comment)
466 {
467 if (comment == NULL)
468 printf("%32s: %16d %-16s\n", option, value, units);
469 else
470 printf("%32s: %16d %-16s (%s)\n", option, value, units, comment);
471 }
472
473 void setup_process_io_policy(int io_tier)
474 {
475 switch(io_tier)
476 {
477 case 0:
478 if (setiopolicy_np(IOPOL_TYPE_DISK, IOPOL_SCOPE_PROCESS, IOPOL_IMPORTANT))
479 goto iopol_error;
480 break;
481 case 1:
482 if (setiopolicy_np(IOPOL_TYPE_DISK, IOPOL_SCOPE_PROCESS, IOPOL_STANDARD))
483 goto iopol_error;
484 break;
485 case 2:
486 if (setiopolicy_np(IOPOL_TYPE_DISK, IOPOL_SCOPE_PROCESS, IOPOL_UTILITY))
487 goto iopol_error;
488 break;
489 case 3:
490 if (setiopolicy_np(IOPOL_TYPE_DISK, IOPOL_SCOPE_PROCESS, IOPOL_THROTTLE))
491 goto iopol_error;
492 break;
493 }
494 return;
495
496 iopol_error:
497 printf("Error setting process-wide I/O policy to %d\n", io_tier);
498 exit(1);
499 }
500
501 int main(int argc, char *argv[])
502 {
503 int i, option = 0;
504 pthread_t thread_list[MAX_THREADS];
505 pthread_t sync_thread;
506 pthread_t throughput_thread;
507 char fname[MAX_FILENAME];
508
509 while((option = getopt(argc, argv,"hc:i:d:t:f:m:j:s:x:l:z:n:a:q:")) != -1) {
510 switch(option) {
511 case 'c':
512 burst_count = atoi(optarg);
513 validate_option(burst_count, 0, INT_MAX, "Burst Count", "I/Os");
514 break;
515 case 'i':
516 inter_burst_duration = atoi(optarg);
517 validate_option(inter_burst_duration, -1, INT_MAX, "Inter Burst duration", "msecs");
518 break;
519 case 'd':
520 inter_io_delay_ms = atoi(optarg);
521 validate_option(inter_io_delay_ms, 0, INT_MAX, "Inter I/O Delay", "msecs");
522 break;
523 case 't':
524 thread_count = atoi(optarg);
525 validate_option(thread_count, 0, MAX_THREADS, "Thread Count", "Threads");
526 break;
527 case 'f':
528 workload_type = atoi(optarg);
529 validate_option(workload_type, 0, 2, "Workload Type", "");
530 break;
531 case 'm':
532 io_mode = atoi(optarg);
533 validate_option(io_mode, 0, 1, "I/O Mode", "");
534 break;
535 case 'j':
536 io_size = atoi(optarg);
537 validate_option(io_size, 0, INT_MAX, "I/O Size", "Bytes");
538 break;
539 case 'h':
540 print_usage();
541 exit(1);
542 case 's':
543 sync_frequency_ms = atoi(optarg);
544 validate_option(sync_frequency_ms, 0, INT_MAX, "Sync. Frequency", "msecs");
545 break;
546 case 'x':
547 test_duration = atoi(optarg);
548 validate_option(test_duration, 0, INT_MAX, "Test duration", "secs");
549 break;
550 case 'l':
551 io_tier = atoi(optarg);
552 validate_option(io_tier, 0, 3, "I/O Tier", "");
553 break;
554 case 'z':
555 file_size = atoi(optarg);
556 validate_option(file_size, 0, INT_MAX, "File Size", "bytes");
557 break;
558 case 'n':
559 user_fname = optarg;
560 user_specified_file = 1;
561 break;
562 case 'a':
563 cached_io_flag = atoi(optarg);
564 validate_option(cached_io_flag, 0, 1, "I/Os cached/no-cached", "");
565 break;
566 case 'q':
567 io_qos_timeout_ms = atoi(optarg);
568 validate_option(io_qos_timeout_ms, 0, INT_MAX, "I/O QoS timeout", "msecs");
569 break;
570 default:
571 printf("Unknown option %c\n", option);
572 print_usage();
573 exit(1);
574 }
575 }
576
577 printf("***********************TEST SETUP*************************\n");
578
579 print_test_setup(burst_count, "Burst Count", "I/Os", 0);
580 print_test_setup(inter_burst_duration, "Inter Burst duration", "msecs", "-1 indicates random burst duration");
581 print_test_setup(inter_io_delay_ms, "Inter I/O Delay", "msecs", 0);
582 print_test_setup(thread_count, "Thread Count", "Threads", 0);
583 print_test_setup(workload_type, "Workload Type", "", "0:R 1:W 2:RW");
584 print_test_setup(io_mode, "I/O Mode", "", "0:Seq. 1:Rnd");
585 print_test_setup(io_size, "I/O Size", "Bytes", 0);
586 print_test_setup(sync_frequency_ms, "Sync. Frequency", "msecs", "0 indicates no sync. thread");
587 print_test_setup(test_duration, "Test duration", "secs", "0 indicates tool waits for Ctrl+C");
588 print_test_setup(io_tier, "I/O Tier", "", 0);
589 print_test_setup(cached_io_flag, "I/O Caching", "", "0 indicates non-cached I/Os");
590 print_test_setup(io_qos_timeout_ms, "I/O QoS Threshold Timeout", "msecs", 0);
591 print_test_setup(0, "File read-aheads", "", "0 indicates read-aheads disabled");
592
593 printf("**********************************************************\n");
594
595 if (user_specified_file == 0) {
596 char dd_command[MAX_CMD_SIZE];
597 for (i=0; i < thread_count; i++) {
598 snprintf(fname, MAX_FILENAME, "iosim-%d-%d", (int)getpid(), i);
599 snprintf(dd_command, MAX_CMD_SIZE, "dd if=/dev/urandom of=%s bs=4096 count=%d", fname, file_size);
600 printf("Creating file %s of size %lld...\n", fname, ((int64_t)file_size * 4096));
601 system_cmd(dd_command);
602 }
603 } else {
604 printf("Using user specified file %s for all threads...\n", user_fname);
605 }
606 system_cmd("purge");
607 setup_process_io_policy(io_tier);
608
609 setup_qos_device();
610
611 printf("**********************************************************\n");
612 printf("Creating threads and generating workload...\n");
613
614 signal(SIGINT, signalHandler);
615 signal(SIGALRM, signalHandler);
616
617 for(i=0; i < thread_count; i++) {
618 if (pthread_create(&thread_list[i], NULL, io_routine, i) < 0) {
619 perror("Could not create I/O thread!\n");
620 exit(1);
621 }
622 }
623
624 if (sync_frequency_ms) {
625 if (pthread_create(&sync_thread, NULL, sync_routine, NULL) < 0) {
626 perror("Could not create sync thread!\n");
627 exit(1);
628 }
629 }
630
631 if (pthread_create(&throughput_thread, NULL, calculate_throughput, NULL) < 0) {
632 perror("Could not throughput calculation thread!\n");
633 exit(1);
634 }
635
636 if(io_qos_timeout_ms > 0) {
637 CFRunLoopRunInMode(kCFRunLoopDefaultMode, (CFTimeInterval)test_duration, false);
638 alarm(1);
639 } else {
640 /* All threads are now initialized */
641 if (test_duration)
642 alarm(test_duration);
643 }
644
645 for(i=0; i < thread_count; i++)
646 pthread_join(thread_list[i], NULL);
647
648 if (sync_frequency_ms)
649 pthread_join(sync_thread, NULL);
650
651 pthread_join(throughput_thread, NULL);
652
653 pthread_exit(0);
654 }
655
656 extern char **environ;
657
658 int system_cmd(char *command)
659 {
660 // workaround for rdar://problem/53281655
661 pid_t pid;
662 char *argv[] = {"sh", "-c", command, NULL};
663 int status;
664 status = posix_spawn(&pid, "/bin/sh", NULL, NULL, argv, environ);
665 if (status == 0) {
666 if (waitpid(pid, &status, 0) != -1) {
667 return status;
668 } else {
669 perror("waitpid");
670 }
671 }
672 return -1;
673 }