X-Git-Url: https://git.kernel.dk/?p=fio.git;a=blobdiff_plain;f=fio.c;h=412f00295b27f6aff1e190d8b2e0bd37d502133b;hp=b53a58ebe1883bbc9ad17f83fd598ce0c81c0c3c;hb=906c8d75eef9247c02d1f1f6771b6fa2338329fa;hpb=88c6ed8085efd1403fd34072311991cb666a5b9e diff --git a/fio.c b/fio.c index b53a58eb..412f0029 100644 --- a/fio.c +++ b/fio.c @@ -103,6 +103,10 @@ static void sig_handler(int sig) } } +/* + * The ->file_map[] contains a map of blocks we have or have not done io + * to yet. Used to make sure we cover the entire range in a fair fashion. + */ static int random_map_free(struct thread_data *td, unsigned long long block) { unsigned int idx = RAND_MAP_IDX(td, block); @@ -111,6 +115,9 @@ static int random_map_free(struct thread_data *td, unsigned long long block) return (td->file_map[idx] & (1UL << bit)) == 0; } +/* + * Return the next free block in the map. + */ static int get_next_free_block(struct thread_data *td, unsigned long long *b) { int i; @@ -130,6 +137,9 @@ static int get_next_free_block(struct thread_data *td, unsigned long long *b) return 1; } +/* + * Mark a given offset as used in the map. + */ static void mark_random_map(struct thread_data *td, struct io_u *io_u) { unsigned long long block = io_u->offset / (unsigned long long) td->min_bs; @@ -155,6 +165,11 @@ static void mark_random_map(struct thread_data *td, struct io_u *io_u) io_u->buflen = blocks * td->min_bs; } +/* + * For random io, generate a random new block and see if it's used. Repeat + * until we find a free one. For sequential io, just return the end of + * the last io issued. + */ static int get_next_offset(struct thread_data *td, unsigned long long *offset) { unsigned long long b, rb; @@ -204,6 +219,9 @@ static unsigned int get_next_buflen(struct thread_data *td) return buflen; } +/* + * Check if we are above the minimum rate given. + */ static int check_min_rate(struct thread_data *td, struct timeval *now) { unsigned long spent; @@ -352,6 +370,11 @@ static void fill_md5(struct verify_header *hdr, void *p, unsigned int len) memcpy(hdr->md5_digest, md5_ctx.hash, sizeof(md5_ctx.hash)); } +/* + * Return the data direction for the next io_u. If the job is a + * mixed read/write workload, check the rwmix cycle and switch if + * necessary. + */ static int get_rw_ddir(struct thread_data *td) { if (td_rw(td)) { @@ -472,6 +495,10 @@ struct io_u *__get_io_u(struct thread_data *td) return io_u; } +/* + * Return an io_u to be processed. Gets a buflen and offset, sets direction, + * etc. The returned io_u is fully ready to be prepped and submitted. + */ static struct io_u *get_io_u(struct thread_data *td) { struct io_u *io_u; @@ -608,6 +635,10 @@ static void ios_completed(struct thread_data *td,struct io_completion_data *icd) } } +/* + * When job exits, we can cancel the in-flight IO if we are using async + * io. Attempt to do so. + */ static void cleanup_pending_aio(struct thread_data *td) { struct timespec ts = { .tv_sec = 0, .tv_nsec = 0}; @@ -661,6 +692,10 @@ static int do_io_u_verify(struct thread_data *td, struct io_u **io_u) return ret; } +/* + * The main verify engine. Runs over the writes we previusly submitted, + * reads the blocks back in, and checks the crc/md5 of the data. + */ static void do_verify(struct thread_data *td) { struct timeval t; @@ -742,7 +777,7 @@ static void do_verify(struct thread_data *td) } /* - * Main IO worker functions. It retrieves io_u's to process and queues + * Main IO worker function. It retrieves io_u's to process and queues * and reaps them, checking for rate and errors along the way. */ static void do_io(struct thread_data *td) @@ -941,19 +976,20 @@ static int init_io_u(struct thread_data *td) return 0; } -static int create_file(struct thread_data *td, unsigned long long size, - int extend) +static int create_file(struct thread_data *td, unsigned long long size) { unsigned long long left; unsigned int bs; - int r, oflags; char *b; + int r; /* * unless specifically asked for overwrite, let normal io extend it */ - if (td_write(td) && !td->overwrite) + if (!td->overwrite) { + td->real_file_size = size; return 0; + } if (!size) { log_err("Need size for create\n"); @@ -962,26 +998,17 @@ static int create_file(struct thread_data *td, unsigned long long size, } temp_stall_ts = 1; + fprintf(f_out, "%s: Laying out IO file (%LuMiB)\n",td->name,size >> 20); - if (!extend) { - oflags = O_CREAT | O_TRUNC; - fprintf(f_out, "%s: Laying out IO file (%LuMiB)\n", td->name, size >> 20); - } else { - oflags = O_APPEND; - fprintf(f_out, "%s: Extending IO file (%Lu -> %LuMiB)\n", td->name, (td->file_size - size) >> 20, td->file_size >> 20); - } - - td->fd = open(td->file_name, O_WRONLY | oflags, 0644); + td->fd = open(td->file_name, O_WRONLY | O_CREAT | O_TRUNC, 0644); if (td->fd < 0) { td_verror(td, errno); - temp_stall_ts = 0; - return 1; + goto done_noclose; } - if (!extend && ftruncate(td->fd, td->file_size) == -1) { + if (ftruncate(td->fd, td->file_size) == -1) { td_verror(td, errno); - temp_stall_ts = 0; - return 1; + goto done; } td->io_size = td->file_size; @@ -1014,10 +1041,12 @@ static int create_file(struct thread_data *td, unsigned long long size, else if (td->create_fsync) fsync(td->fd); - temp_stall_ts = 0; + free(b); +done: close(td->fd); td->fd = -1; - free(b); +done_noclose: + temp_stall_ts = 0; return 0; } @@ -1025,15 +1054,17 @@ static int file_size(struct thread_data *td) { struct stat st; - if (fstat(td->fd, &st) == -1) { - td_verror(td, errno); - return 1; - } + if (td->overwrite) { + if (fstat(td->fd, &st) == -1) { + td_verror(td, errno); + return 1; + } - td->real_file_size = st.st_size; + td->real_file_size = st.st_size; - if (!td->file_size || td->file_size > td->real_file_size) - td->file_size = td->real_file_size; + if (!td->file_size || td->file_size > td->real_file_size) + td->file_size = td->real_file_size; + } td->file_size -= td->file_offset; return 0; @@ -1176,13 +1207,12 @@ static int setup_file(struct thread_data *td) td_verror(td, ENOENT); return 1; } - if (create_file(td, td->file_size, 0)) + if (create_file(td, td->file_size)) + return 1; + } else if (td->filetype == FIO_TYPE_FILE && + st.st_size < (off_t) td->file_size) { + if (create_file(td, td->file_size)) return 1; - } else if (td->filetype == FIO_TYPE_FILE) { - if (st.st_size < (off_t) td->file_size) { - if (create_file(td, td->file_size - st.st_size, 1)) - return 1; - } } if (td->odirect) @@ -1286,6 +1316,10 @@ static void clear_io_state(struct thread_data *td) memset(td->file_map, 0, td->num_maps * sizeof(long)); } +/* + * Entry point for the thread based jobs. The process based jobs end up + * here as well, after a little setup. + */ static void *thread_main(void *data) { struct thread_data *td = data; @@ -1408,6 +1442,10 @@ err: } +/* + * We cannot pass the td data into a forked process, so attach the td and + * pass it to the thread worker. + */ static void *fork_main(int shmid, int offset) { struct thread_data *td; @@ -1425,6 +1463,9 @@ static void *fork_main(int shmid, int offset) return NULL; } +/* + * Sets the status of the 'td' in the printed status map. + */ static void check_str_update(struct thread_data *td) { char c = run_str[td->thread_number - 1]; @@ -1476,6 +1517,9 @@ static void check_str_update(struct thread_data *td) run_str[td->thread_number - 1] = c; } +/* + * Convert seconds to a printable string. + */ static void eta_to_str(char *str, int eta_sec) { unsigned int d, h, m, s; @@ -1504,6 +1548,9 @@ static void eta_to_str(char *str, int eta_sec) str += sprintf(str, "%02ds", s); } +/* + * Best effort calculation of the estimated pending runtime of a job. + */ static int thread_eta(struct thread_data *td, unsigned long elapsed) { unsigned long long bytes_total, bytes_done; @@ -1569,6 +1616,10 @@ static int thread_eta(struct thread_data *td, unsigned long elapsed) return eta_sec; } +/* + * Print status of the jobs we know about. This includes rate estimates, + * ETA, thread state, etc. + */ static void print_thread_status(void) { unsigned long elapsed = time_since_now(&genesis); @@ -1576,7 +1627,7 @@ static void print_thread_status(void) char eta_str[32]; double perc = 0.0; - if (temp_stall_ts) + if (temp_stall_ts || terse_output) return; eta_secs = malloc(thread_number * sizeof(int)); @@ -1637,6 +1688,9 @@ static void print_thread_status(void) free(eta_secs); } +/* + * Run over the job map and reap the threads that have exited, if any. + */ static void reap_threads(int *nr_running, int *t_rate, int *m_rate) { int i; @@ -1708,6 +1762,9 @@ static void *fio_pin_memory(void) return ptr; } +/* + * Main function for kicking off and reaping jobs, as needed. + */ static void run_threads(void) { struct thread_data *td; @@ -1717,8 +1774,10 @@ static void run_threads(void) mlocked_mem = fio_pin_memory(); - printf("Starting %d thread%s\n", thread_number, thread_number > 1 ? "s" : ""); - fflush(stdout); + if (!terse_output) { + printf("Starting %d thread%s\n", thread_number, thread_number > 1 ? "s" : ""); + fflush(stdout); + } signal(SIGINT, sig_handler); signal(SIGALRM, sig_handler);