Commit | Line | Data |
---|---|---|
27c32a38 JA |
1 | #include <stdio.h> |
2 | #include <stdlib.h> | |
3 | #include <unistd.h> | |
4 | #include <fcntl.h> | |
5 | #include <ctype.h> | |
6 | #include <string.h> | |
7 | #include <errno.h> | |
8 | #include <sys/ipc.h> | |
9 | #include <sys/shm.h> | |
10 | ||
11 | #include "fio.h" | |
12 | ||
13 | #define DEF_BS (4096) | |
14 | #define DEF_TIMEOUT (0) | |
15 | #define DEF_RATE_CYCLE (1000) | |
16 | #define DEF_ODIRECT (1) | |
17 | #define DEF_SEQUENTIAL (1) | |
18 | #define DEF_RAND_REPEAT (1) | |
19 | #define DEF_OVERWRITE (0) | |
20 | #define DEF_CREATE (1) | |
21 | #define DEF_INVALIDATE (1) | |
22 | #define DEF_SYNCIO (0) | |
23 | #define DEF_RANDSEED (0xb1899bedUL) | |
24 | #define DEF_BWAVGTIME (500) | |
25 | #define DEF_CREATE_SER (1) | |
26 | #define DEF_CREATE_FSYNC (1) | |
27 | #define DEF_LOOPS (1) | |
28 | #define DEF_VERIFY (0) | |
29 | #define DEF_STONEWALL (0) | |
30 | #define DEF_NUMJOBS (1) | |
189873de | 31 | #define DEF_USE_THREAD (0) |
27c32a38 JA |
32 | |
33 | static int repeatable = DEF_RAND_REPEAT; | |
34 | static char *ini_file; | |
35 | static int max_jobs = MAX_JOBS; | |
36 | ||
37 | struct thread_data def_thread; | |
38 | struct thread_data *threads = NULL; | |
39 | ||
40 | int rate_quit = 0; | |
41 | int write_lat_log = 0; | |
42 | int write_bw_log = 0; | |
43 | int exitall_on_terminate = 0; | |
44 | ||
45 | static int setup_rate(struct thread_data *td) | |
46 | { | |
47 | int nr_reads_per_sec; | |
48 | ||
49 | if (!td->rate) | |
50 | return 0; | |
51 | ||
52 | if (td->rate < td->ratemin) { | |
53 | fprintf(stderr, "min rate larger than nominal rate\n"); | |
54 | return -1; | |
55 | } | |
56 | ||
57 | nr_reads_per_sec = (td->rate * 1024) / td->min_bs; | |
58 | td->rate_usec_cycle = 1000000 / nr_reads_per_sec; | |
59 | td->rate_pending_usleep = 0; | |
60 | return 0; | |
61 | } | |
62 | ||
63 | static void setup_log(struct io_log **log) | |
64 | { | |
65 | struct io_log *l = malloc(sizeof(*l)); | |
66 | ||
67 | l->nr_samples = 0; | |
68 | l->max_samples = 1024; | |
69 | l->log = malloc(l->max_samples * sizeof(struct io_sample)); | |
70 | *log = l; | |
71 | } | |
72 | ||
73 | void finish_log(struct thread_data *td, struct io_log *log, const char *name) | |
74 | { | |
75 | char file_name[128]; | |
76 | FILE *f; | |
77 | unsigned int i; | |
78 | ||
79 | sprintf(file_name, "client%d_%s.log", td->thread_number, name); | |
80 | f = fopen(file_name, "w"); | |
81 | if (!f) { | |
82 | perror("fopen log"); | |
83 | return; | |
84 | } | |
85 | ||
86 | for (i = 0; i < log->nr_samples; i++) | |
87 | fprintf(f, "%lu, %lu\n", log->log[i].time, log->log[i].val); | |
88 | ||
89 | fclose(f); | |
90 | free(log->log); | |
91 | free(log); | |
92 | } | |
93 | ||
94 | static struct thread_data *get_new_job(int global, struct thread_data *parent) | |
95 | { | |
96 | struct thread_data *td; | |
97 | ||
98 | if (global) | |
99 | return &def_thread; | |
100 | if (thread_number >= max_jobs) | |
101 | return NULL; | |
102 | ||
103 | td = &threads[thread_number++]; | |
104 | memset(td, 0, sizeof(*td)); | |
105 | ||
106 | sprintf(td->directory, "."); | |
107 | ||
108 | td->fd = -1; | |
109 | td->thread_number = thread_number; | |
110 | ||
111 | td->ddir = parent->ddir; | |
112 | td->ioprio = parent->ioprio; | |
113 | td->sequential = parent->sequential; | |
114 | td->bs = parent->bs; | |
115 | td->min_bs = parent->min_bs; | |
116 | td->max_bs = parent->max_bs; | |
117 | td->odirect = parent->odirect; | |
118 | td->thinktime = parent->thinktime; | |
119 | td->fsync_blocks = parent->fsync_blocks; | |
120 | td->start_delay = parent->start_delay; | |
121 | td->timeout = parent->timeout; | |
122 | td->use_aio = parent->use_aio; | |
123 | td->create_file = parent->create_file; | |
124 | td->overwrite = parent->overwrite; | |
125 | td->invalidate_cache = parent->invalidate_cache; | |
126 | td->file_size = parent->file_size; | |
127 | td->file_offset = parent->file_offset; | |
128 | td->rate = parent->rate; | |
129 | td->ratemin = parent->ratemin; | |
130 | td->ratecycle = parent->ratecycle; | |
131 | td->aio_depth = parent->aio_depth; | |
132 | td->sync_io = parent->sync_io; | |
133 | td->mem_type = parent->mem_type; | |
134 | td->bw_avg_time = parent->bw_avg_time; | |
135 | td->create_serialize = parent->create_serialize; | |
136 | td->create_fsync = parent->create_fsync; | |
137 | td->loops = parent->loops; | |
138 | td->verify = parent->verify; | |
139 | td->stonewall = parent->stonewall; | |
140 | td->numjobs = parent->numjobs; | |
189873de | 141 | td->use_thread = parent->use_thread; |
27c32a38 JA |
142 | memcpy(&td->cpumask, &parent->cpumask, sizeof(td->cpumask)); |
143 | ||
144 | return td; | |
145 | } | |
146 | ||
147 | static void put_job(struct thread_data *td) | |
148 | { | |
149 | memset(&threads[td->thread_number - 1], 0, sizeof(*td)); | |
150 | thread_number--; | |
151 | } | |
152 | ||
153 | static int add_job(struct thread_data *td, const char *jobname, int prioclass, | |
154 | int prio) | |
155 | { | |
c4c8f7b3 | 156 | struct stat sb; |
27c32a38 JA |
157 | int numjobs; |
158 | ||
159 | if (td == &def_thread) | |
160 | return 0; | |
161 | ||
c4c8f7b3 JA |
162 | td->filetype = FIO_TYPE_FILE; |
163 | if (!stat(jobname, &sb) && S_ISBLK(sb.st_mode)) | |
164 | td->filetype = FIO_TYPE_BD; | |
165 | ||
166 | if (td->filetype == FIO_TYPE_FILE) | |
167 | sprintf(td->file_name, "%s/%s.%d", td->directory, jobname, td->thread_number); | |
168 | else | |
169 | strcpy(td->file_name, jobname); | |
170 | ||
27c32a38 JA |
171 | sem_init(&td->mutex, 1, 0); |
172 | td->ioprio = (prioclass << IOPRIO_CLASS_SHIFT) | prio; | |
173 | ||
174 | td->clat_stat.min_val = ULONG_MAX; | |
175 | td->slat_stat.min_val = ULONG_MAX; | |
176 | td->bw_stat.min_val = ULONG_MAX; | |
177 | ||
178 | run_str[td->thread_number - 1] = 'P'; | |
179 | ||
180 | if (td->use_aio && !td->aio_depth) | |
181 | td->aio_depth = 1; | |
182 | ||
183 | if (td->min_bs == -1U) | |
184 | td->min_bs = td->bs; | |
185 | if (td->max_bs == -1U) | |
186 | td->max_bs = td->bs; | |
187 | if (td_read(td)) | |
188 | td->verify = 0; | |
189 | ||
190 | if (td->stonewall && td->thread_number > 1) | |
191 | groupid++; | |
192 | ||
193 | td->groupid = groupid; | |
194 | ||
195 | if (setup_rate(td)) | |
196 | goto err; | |
197 | ||
198 | if (write_lat_log) | |
199 | setup_log(&td->lat_log); | |
200 | if (write_bw_log) | |
201 | setup_log(&td->bw_log); | |
202 | ||
203 | printf("Client%d (g=%d): rw=%d, prio=%d/%d, seq=%d, odir=%d, bs=%d-%d, rate=%d, aio=%d, aio_depth=%d\n", td->thread_number, td->groupid, td->ddir, prioclass, prio, td->sequential, td->odirect, td->min_bs, td->max_bs, td->rate, td->use_aio, td->aio_depth); | |
204 | ||
205 | /* | |
206 | * recurse add identical jobs, clear numjobs and stonewall options | |
207 | * as they don't apply to sub-jobs | |
208 | */ | |
209 | numjobs = td->numjobs; | |
210 | while (--numjobs) { | |
211 | struct thread_data *td_new = get_new_job(0, td); | |
212 | ||
213 | if (!td_new) | |
214 | goto err; | |
215 | ||
216 | td_new->numjobs = 1; | |
217 | td_new->stonewall = 0; | |
218 | ||
219 | if (add_job(td_new, jobname, prioclass, prio)) | |
220 | goto err; | |
221 | } | |
222 | return 0; | |
223 | err: | |
224 | put_job(td); | |
225 | return -1; | |
226 | } | |
227 | ||
228 | int init_random_state(struct thread_data *td) | |
229 | { | |
230 | unsigned long seed; | |
231 | int fd, num_maps, blocks; | |
232 | ||
233 | fd = open("/dev/random", O_RDONLY); | |
234 | if (fd == -1) { | |
235 | td->error = errno; | |
236 | return 1; | |
237 | } | |
238 | ||
239 | if (read(fd, &seed, sizeof(seed)) < (int) sizeof(seed)) { | |
240 | td->error = EIO; | |
241 | close(fd); | |
242 | return 1; | |
243 | } | |
244 | ||
245 | close(fd); | |
246 | ||
247 | srand48_r(seed, &td->bsrange_state); | |
248 | srand48_r(seed, &td->verify_state); | |
249 | ||
250 | if (td->sequential) | |
251 | return 0; | |
252 | ||
253 | if (repeatable) | |
254 | seed = DEF_RANDSEED; | |
255 | ||
256 | blocks = (td->io_size + td->min_bs - 1) / td->min_bs; | |
257 | num_maps = blocks / BLOCKS_PER_MAP; | |
258 | td->file_map = malloc(num_maps * sizeof(long)); | |
259 | td->num_maps = num_maps; | |
260 | memset(td->file_map, 0, num_maps * sizeof(long)); | |
261 | ||
262 | srand48_r(seed, &td->random_state); | |
263 | return 0; | |
264 | } | |
265 | ||
266 | static void fill_cpu_mask(cpu_set_t cpumask, int cpu) | |
267 | { | |
268 | unsigned int i; | |
269 | ||
270 | CPU_ZERO(&cpumask); | |
271 | ||
272 | for (i = 0; i < sizeof(int) * 8; i++) { | |
273 | if ((1 << i) & cpu) | |
274 | CPU_SET(i, &cpumask); | |
275 | } | |
276 | } | |
277 | ||
278 | static unsigned long get_mult(char c) | |
279 | { | |
280 | switch (c) { | |
281 | case 'k': | |
282 | case 'K': | |
283 | return 1024; | |
284 | case 'm': | |
285 | case 'M': | |
286 | return 1024 * 1024; | |
287 | case 'g': | |
288 | case 'G': | |
289 | return 1024 * 1024 * 1024; | |
290 | default: | |
291 | return 1; | |
292 | } | |
293 | } | |
294 | ||
295 | /* | |
296 | * convert string after '=' into decimal value, noting any size suffix | |
297 | */ | |
298 | static int str_cnv(char *p, unsigned long long *val) | |
299 | { | |
300 | char *str; | |
301 | int len; | |
302 | ||
303 | str = strstr(p, "="); | |
304 | if (!str) | |
305 | return 1; | |
306 | ||
307 | str++; | |
308 | len = strlen(str); | |
309 | ||
310 | *val = strtoul(str, NULL, 10); | |
311 | if (*val == ULONG_MAX && errno == ERANGE) | |
312 | return 1; | |
313 | ||
314 | *val *= get_mult(str[len - 2]); | |
315 | return 0; | |
316 | } | |
317 | ||
318 | static int check_strcnv(char *p, char *name, unsigned long long *val) | |
319 | { | |
320 | if (!strstr(p, name)) | |
321 | return 1; | |
322 | ||
323 | return str_cnv(p, val); | |
324 | } | |
325 | ||
326 | static int check_str(char *p, char *name, char *option) | |
327 | { | |
328 | char *s = strstr(p, name); | |
329 | ||
330 | if (!s) | |
331 | return 1; | |
332 | ||
333 | s += strlen(name); | |
334 | if (strstr(s, option)) | |
335 | return 0; | |
336 | ||
337 | return 1; | |
338 | } | |
339 | ||
340 | static int check_strstore(char *p, char *name, char *dest) | |
341 | { | |
342 | char *s = strstr(p, name); | |
343 | ||
344 | if (!s) | |
345 | return 1; | |
346 | ||
347 | s = strstr(p, "="); | |
348 | if (!s) | |
349 | return 1; | |
350 | ||
351 | s++; | |
352 | while (isblank(*s)) | |
353 | s++; | |
354 | ||
355 | strcpy(dest, s); | |
356 | ||
357 | s = dest + strlen(dest) - 1; | |
358 | while (isblank(*s)) { | |
359 | *s = '\0'; | |
360 | s--; | |
361 | } | |
362 | ||
363 | return 0; | |
364 | } | |
365 | ||
366 | static int check_range(char *p, char *name, unsigned long *s, unsigned long *e) | |
367 | { | |
368 | char str[128]; | |
369 | char s1, s2; | |
370 | ||
371 | sprintf(str, "%s=%%lu%%c-%%lu%%c", name); | |
372 | if (sscanf(p, str, s, &s1, e, &s2) == 4) { | |
373 | *s *= get_mult(s1); | |
374 | *e *= get_mult(s2); | |
375 | return 0; | |
376 | } | |
377 | ||
378 | sprintf(str, "%s = %%lu%%c-%%lu%%c", name); | |
379 | if (sscanf(p, str, s, &s1, e, &s2) == 4) { | |
380 | *s *= get_mult(s1); | |
381 | *e *= get_mult(s2); | |
382 | return 0; | |
383 | } | |
384 | ||
385 | sprintf(str, "%s=%%lu-%%lu", name); | |
386 | if (sscanf(p, str, s, e) == 2) | |
387 | return 0; | |
388 | ||
389 | sprintf(str, "%s = %%lu-%%lu", name); | |
390 | if (sscanf(p, str, s, e) == 2) | |
391 | return 0; | |
392 | ||
393 | return 1; | |
394 | ||
395 | } | |
396 | ||
397 | static int check_int(char *p, char *name, unsigned int *val) | |
398 | { | |
399 | char str[128]; | |
400 | ||
401 | sprintf(str, "%s=%%d", name); | |
402 | if (sscanf(p, str, val) == 1) | |
403 | return 0; | |
404 | ||
405 | sprintf(str, "%s = %%d", name); | |
406 | if (sscanf(p, str, val) == 1) | |
407 | return 0; | |
408 | ||
409 | return 1; | |
410 | } | |
411 | ||
1d2123e0 JA |
412 | static int check_strset(char *p, char *name) |
413 | { | |
5812ff31 | 414 | return strncmp(p, name, strlen(name)); |
1d2123e0 JA |
415 | } |
416 | ||
27c32a38 JA |
417 | static int is_empty_or_comment(char *line) |
418 | { | |
419 | unsigned int i; | |
420 | ||
421 | for (i = 0; i < strlen(line); i++) { | |
422 | if (line[i] == ';') | |
423 | return 1; | |
424 | if (!isspace(line[i]) && !iscntrl(line[i])) | |
425 | return 0; | |
426 | } | |
427 | ||
428 | return 1; | |
429 | } | |
430 | ||
431 | int parse_jobs_ini(char *file) | |
432 | { | |
433 | unsigned int prioclass, prio, cpu, global; | |
434 | unsigned long long ull; | |
435 | unsigned long ul1, ul2; | |
436 | struct thread_data *td; | |
437 | char *string, *name; | |
438 | fpos_t off; | |
439 | FILE *f; | |
440 | char *p; | |
441 | ||
442 | f = fopen(file, "r"); | |
443 | if (!f) { | |
444 | perror("fopen"); | |
445 | return 1; | |
446 | } | |
447 | ||
448 | string = malloc(4096); | |
449 | name = malloc(256); | |
450 | ||
451 | while ((p = fgets(string, 4096, f)) != NULL) { | |
452 | if (is_empty_or_comment(p)) | |
453 | continue; | |
454 | if (sscanf(p, "[%s]", name) != 1) | |
455 | continue; | |
456 | ||
457 | global = !strncmp(name, "global", 6); | |
458 | ||
459 | name[strlen(name) - 1] = '\0'; | |
460 | ||
461 | td = get_new_job(global, &def_thread); | |
462 | if (!td) | |
463 | return 1; | |
464 | ||
465 | prioclass = 2; | |
466 | prio = 4; | |
467 | ||
468 | fgetpos(f, &off); | |
469 | while ((p = fgets(string, 4096, f)) != NULL) { | |
470 | if (is_empty_or_comment(p)) | |
471 | continue; | |
472 | if (strstr(p, "[")) | |
473 | break; | |
474 | if (!check_int(p, "rw", &td->ddir)) { | |
475 | fgetpos(f, &off); | |
476 | continue; | |
477 | } | |
478 | if (!check_int(p, "prio", &prio)) { | |
479 | fgetpos(f, &off); | |
480 | continue; | |
481 | } | |
482 | if (!check_int(p, "prioclass", &prioclass)) { | |
483 | fgetpos(f, &off); | |
484 | continue; | |
485 | } | |
486 | if (!check_int(p, "direct", &td->odirect)) { | |
487 | fgetpos(f, &off); | |
488 | continue; | |
489 | } | |
490 | if (!check_int(p, "rate", &td->rate)) { | |
491 | fgetpos(f, &off); | |
492 | continue; | |
493 | } | |
494 | if (!check_int(p, "ratemin", &td->ratemin)) { | |
495 | fgetpos(f, &off); | |
496 | continue; | |
497 | } | |
498 | if (!check_int(p, "ratecycle", &td->ratecycle)) { | |
499 | fgetpos(f, &off); | |
500 | continue; | |
501 | } | |
502 | if (!check_int(p, "thinktime", &td->thinktime)) { | |
503 | fgetpos(f, &off); | |
504 | continue; | |
505 | } | |
506 | if (!check_int(p, "cpumask", &cpu)) { | |
507 | fill_cpu_mask(td->cpumask, cpu); | |
508 | fgetpos(f, &off); | |
509 | continue; | |
510 | } | |
511 | if (!check_int(p, "fsync", &td->fsync_blocks)) { | |
512 | fgetpos(f, &off); | |
513 | continue; | |
514 | } | |
515 | if (!check_int(p, "startdelay", &td->start_delay)) { | |
516 | fgetpos(f, &off); | |
517 | continue; | |
518 | } | |
519 | if (!check_int(p, "timeout", &td->timeout)) { | |
520 | fgetpos(f, &off); | |
521 | continue; | |
522 | } | |
523 | if (!check_int(p, "invalidate",&td->invalidate_cache)) { | |
524 | fgetpos(f, &off); | |
525 | continue; | |
526 | } | |
527 | if (!check_int(p, "aio_depth", &td->aio_depth)) { | |
528 | fgetpos(f, &off); | |
529 | continue; | |
530 | } | |
531 | if (!check_int(p, "sync", &td->sync_io)) { | |
532 | fgetpos(f, &off); | |
533 | continue; | |
534 | } | |
535 | if (!check_int(p, "bwavgtime", &td->bw_avg_time)) { | |
536 | fgetpos(f, &off); | |
537 | continue; | |
538 | } | |
539 | if (!check_int(p, "create_serialize", &td->create_serialize)) { | |
540 | fgetpos(f, &off); | |
541 | continue; | |
542 | } | |
543 | if (!check_int(p, "create_fsync", &td->create_fsync)) { | |
544 | fgetpos(f, &off); | |
545 | continue; | |
546 | } | |
547 | if (!check_int(p, "loops", &td->loops)) { | |
548 | fgetpos(f, &off); | |
549 | continue; | |
550 | } | |
551 | if (!check_int(p, "verify", &td->verify)) { | |
552 | fgetpos(f, &off); | |
553 | continue; | |
554 | } | |
555 | if (!check_int(p, "numjobs", &td->numjobs)) { | |
556 | fgetpos(f, &off); | |
557 | continue; | |
558 | } | |
559 | if (!check_range(p, "bsrange", &ul1, &ul2)) { | |
560 | if (ul1 & 511) | |
561 | printf("bad min block size, must be a multiple of 512\n"); | |
562 | else | |
563 | td->min_bs = ul1; | |
564 | if (ul2 & 511) | |
565 | printf("bad max block size, must be a multiple of 512\n"); | |
566 | else | |
567 | td->max_bs = ul2; | |
568 | fgetpos(f, &off); | |
569 | continue; | |
570 | } | |
571 | if (!check_strcnv(p, "bs", &ull)) { | |
572 | if (ull & 511) | |
573 | printf("bad block size, must be a multiple of 512\n"); | |
574 | else | |
575 | td->bs = ull; | |
576 | fgetpos(f, &off); | |
577 | continue; | |
578 | } | |
579 | if (!check_strcnv(p, "size", &td->file_size)) { | |
580 | fgetpos(f, &off); | |
581 | continue; | |
582 | } | |
583 | if (!check_strcnv(p, "offset", &td->file_offset)) { | |
584 | fgetpos(f, &off); | |
585 | continue; | |
586 | } | |
587 | if (!check_strstore(p, "directory", td->directory)) { | |
588 | fgetpos(f, &off); | |
589 | continue; | |
590 | } | |
591 | if (!check_str(p, "mem", "malloc")) { | |
592 | td->mem_type = MEM_MALLOC; | |
593 | fgetpos(f, &off); | |
594 | continue; | |
595 | } | |
596 | if (!check_str(p, "mem", "shm")) { | |
597 | td->mem_type = MEM_SHM; | |
598 | fgetpos(f, &off); | |
599 | continue; | |
600 | } | |
1d2123e0 | 601 | if (!check_strset(p, "sequential")) { |
27c32a38 JA |
602 | td->sequential = 1; |
603 | fgetpos(f, &off); | |
604 | continue; | |
605 | } | |
1d2123e0 | 606 | if (!check_strset(p, "random")) { |
27c32a38 JA |
607 | td->sequential = 0; |
608 | fgetpos(f, &off); | |
609 | continue; | |
610 | } | |
1d2123e0 | 611 | if (!check_strset(p, "aio")) { |
27c32a38 JA |
612 | td->use_aio = 1; |
613 | fgetpos(f, &off); | |
614 | continue; | |
615 | } | |
1d2123e0 | 616 | if (!check_strset(p, "create")) { |
27c32a38 JA |
617 | td->create_file = 1; |
618 | fgetpos(f, &off); | |
619 | continue; | |
620 | } | |
1d2123e0 | 621 | if (!check_strset(p, "overwrite")) { |
27c32a38 JA |
622 | td->overwrite = 1; |
623 | fgetpos(f, &off); | |
624 | continue; | |
625 | } | |
1d2123e0 | 626 | if (!check_strset(p, "exitall")) { |
27c32a38 JA |
627 | exitall_on_terminate = 1; |
628 | fgetpos(f, &off); | |
629 | continue; | |
630 | } | |
1d2123e0 | 631 | if (!check_strset(p, "stonewall")) { |
27c32a38 JA |
632 | td->stonewall = 1; |
633 | fgetpos(f, &off); | |
634 | continue; | |
635 | } | |
1d2123e0 | 636 | if (!check_strset(p, "thread")) { |
189873de JA |
637 | td->use_thread = 1; |
638 | fgetpos(f, &off); | |
639 | continue; | |
640 | } | |
641 | ||
27c32a38 JA |
642 | printf("Client%d: bad option %s\n",td->thread_number,p); |
643 | } | |
644 | fsetpos(f, &off); | |
645 | ||
646 | if (add_job(td, name, prioclass, prio)) | |
647 | return 1; | |
648 | } | |
649 | ||
650 | free(string); | |
651 | free(name); | |
652 | fclose(f); | |
653 | return 0; | |
654 | } | |
655 | ||
656 | static int fill_def_thread(void) | |
657 | { | |
658 | memset(&def_thread, 0, sizeof(def_thread)); | |
659 | ||
660 | if (sched_getaffinity(getpid(), sizeof(cpu_set_t), &def_thread.cpumask) == -1) { | |
661 | perror("sched_getaffinity"); | |
662 | return 1; | |
663 | } | |
664 | ||
665 | /* | |
666 | * fill globals | |
667 | */ | |
668 | def_thread.ddir = DDIR_READ; | |
669 | def_thread.bs = DEF_BS; | |
670 | def_thread.min_bs = -1; | |
671 | def_thread.max_bs = -1; | |
672 | def_thread.odirect = DEF_ODIRECT; | |
673 | def_thread.ratecycle = DEF_RATE_CYCLE; | |
674 | def_thread.sequential = DEF_SEQUENTIAL; | |
675 | def_thread.timeout = DEF_TIMEOUT; | |
676 | def_thread.create_file = DEF_CREATE; | |
677 | def_thread.overwrite = DEF_OVERWRITE; | |
678 | def_thread.invalidate_cache = DEF_INVALIDATE; | |
679 | def_thread.sync_io = DEF_SYNCIO; | |
680 | def_thread.mem_type = MEM_MALLOC; | |
681 | def_thread.bw_avg_time = DEF_BWAVGTIME; | |
682 | def_thread.create_serialize = DEF_CREATE_SER; | |
683 | def_thread.create_fsync = DEF_CREATE_FSYNC; | |
684 | def_thread.loops = DEF_LOOPS; | |
685 | def_thread.verify = DEF_VERIFY; | |
686 | def_thread.stonewall = DEF_STONEWALL; | |
687 | def_thread.numjobs = DEF_NUMJOBS; | |
189873de | 688 | def_thread.use_thread = DEF_USE_THREAD; |
27c32a38 JA |
689 | |
690 | return 0; | |
691 | } | |
692 | ||
693 | static void parse_cmd_line(int argc, char *argv[]) | |
694 | { | |
695 | int i; | |
696 | ||
697 | for (i = 1; i < argc; i++) { | |
698 | char *parm = argv[i]; | |
699 | ||
700 | if (parm[0] != '-') | |
701 | break; | |
702 | ||
703 | parm++; | |
704 | switch (*parm) { | |
705 | case 's': | |
706 | parm++; | |
707 | def_thread.sequential = !!atoi(parm); | |
708 | break; | |
709 | case 'b': | |
710 | parm++; | |
711 | def_thread.bs = atoi(parm); | |
712 | def_thread.bs <<= 10; | |
713 | if (!def_thread.bs) { | |
714 | printf("bad block size\n"); | |
715 | def_thread.bs = DEF_BS; | |
716 | } | |
717 | break; | |
718 | case 't': | |
719 | parm++; | |
720 | def_thread.timeout = atoi(parm); | |
721 | break; | |
722 | case 'r': | |
723 | parm++; | |
724 | repeatable = !!atoi(parm); | |
725 | break; | |
726 | case 'R': | |
727 | parm++; | |
728 | rate_quit = !!atoi(parm); | |
729 | break; | |
730 | case 'o': | |
731 | parm++; | |
732 | def_thread.odirect = !!atoi(parm); | |
733 | break; | |
734 | case 'f': | |
735 | if (i + 1 >= argc) { | |
736 | printf("-f needs file as arg\n"); | |
737 | break; | |
738 | } | |
739 | ini_file = strdup(argv[i+1]); | |
740 | i++; | |
741 | break; | |
742 | case 'l': | |
743 | write_lat_log = 1; | |
744 | break; | |
745 | case 'w': | |
746 | write_bw_log = 1; | |
747 | break; | |
748 | default: | |
749 | printf("bad option %s\n", argv[i]); | |
750 | break; | |
751 | } | |
752 | } | |
753 | } | |
754 | ||
755 | static void free_shm(void) | |
756 | { | |
757 | struct shmid_ds sbuf; | |
758 | ||
759 | if (threads) { | |
760 | shmdt(threads); | |
761 | threads = NULL; | |
762 | shmctl(shm_id, IPC_RMID, &sbuf); | |
763 | } | |
764 | } | |
765 | ||
766 | static int setup_thread_area(void) | |
767 | { | |
768 | /* | |
769 | * 1024 is too much on some machines, scale max_jobs if | |
770 | * we get a failure that looks like too large a shm segment | |
771 | */ | |
772 | do { | |
773 | int s = max_jobs * sizeof(struct thread_data); | |
774 | ||
775 | shm_id = shmget(0, s, IPC_CREAT | 0600); | |
776 | if (shm_id != -1) | |
777 | break; | |
778 | if (errno != EINVAL) { | |
779 | perror("shmget"); | |
780 | break; | |
781 | } | |
782 | ||
783 | max_jobs >>= 1; | |
784 | } while (max_jobs); | |
785 | ||
786 | if (shm_id == -1) | |
787 | return 1; | |
788 | ||
789 | threads = shmat(shm_id, NULL, 0); | |
790 | if (threads == (void *) -1) { | |
791 | perror("shmat"); | |
792 | return 1; | |
793 | } | |
794 | ||
795 | atexit(free_shm); | |
796 | return 0; | |
797 | } | |
798 | ||
799 | int parse_options(int argc, char *argv[]) | |
800 | { | |
801 | if (setup_thread_area()) | |
802 | return 1; | |
803 | if (fill_def_thread()) | |
804 | return 1; | |
805 | ||
806 | parse_cmd_line(argc, argv); | |
807 | ||
808 | if (!ini_file) { | |
809 | printf("Need job file\n"); | |
810 | return 1; | |
811 | } | |
812 | ||
813 | if (parse_jobs_ini(ini_file)) | |
814 | return 1; | |
815 | ||
816 | return 0; | |
817 | } |