From: Jens Axboe Date: Wed, 13 Apr 2016 18:25:52 +0000 (-0600) Subject: Fix verify state for multiple files X-Git-Tag: fio-2.9~10 X-Git-Url: https://git.kernel.dk/?p=fio.git;a=commitdiff_plain;h=94a6e1bb4e7d8e7fee66374841634b0f871c6d6d;ds=inline Fix verify state for multiple files Fio currently only logs the completions on a per-job basis, but that breaks down if the job uses multiple files and we rely on the verify state save/load to check when to stop verifying. This patch extends the verify state format to be version 3, and removes support for v1/v2. The new version has support for multiple files. Signed-off-by: Jens Axboe --- diff --git a/backend.c b/backend.c index e093f75d..1723b8f4 100644 --- a/backend.c +++ b/backend.c @@ -1070,6 +1070,41 @@ reap: bytes_done[i] = td->bytes_done[i] - bytes_done[i]; } +static void free_file_completion_logging(struct thread_data *td) +{ + struct fio_file *f; + unsigned int i; + + for_each_file(td, f, i) { + if (!f->last_write_comp) + break; + sfree(f->last_write_comp); + } +} + +static int init_file_completion_logging(struct thread_data *td, + unsigned int depth) +{ + struct fio_file *f; + unsigned int i; + + if (td->o.verify == VERIFY_NONE || !td->o.verify_state_save) + return 0; + + for_each_file(td, f, i) { + f->last_write_comp = scalloc(depth, sizeof(uint64_t)); + if (!f->last_write_comp) + goto cleanup; + } + + return 0; + +cleanup: + free_file_completion_logging(td); + log_err("fio: failed to alloc write comp data\n"); + return 1; +} + static void cleanup_io_u(struct thread_data *td) { struct io_u *io_u; @@ -1088,8 +1123,7 @@ static void cleanup_io_u(struct thread_data *td) io_u_qexit(&td->io_u_freelist); io_u_qexit(&td->io_u_all); - if (td->last_write_comp) - sfree(td->last_write_comp); + free_file_completion_logging(td); } static int init_io_u(struct thread_data *td) @@ -1206,13 +1240,8 @@ static int init_io_u(struct thread_data *td) p += max_bs; } - if (td->o.verify != VERIFY_NONE) { - td->last_write_comp = scalloc(max_units, sizeof(uint64_t)); - if (!td->last_write_comp) { - log_err("fio: failed to alloc write comp data\n"); - return 1; - } - } + if (init_file_completion_logging(td, max_units)) + return 1; return 0; } @@ -1964,12 +1993,11 @@ static int fio_verify_load_state(struct thread_data *td) if (is_backend) { void *data; - int ver; ret = fio_server_get_verify_state(td->o.name, - td->thread_number - 1, &data, &ver); + td->thread_number - 1, &data); if (!ret) - verify_convert_assign_state(td, data, ver); + verify_assign_state(td, data); } else ret = verify_load_state(td, "local"); diff --git a/file.h b/file.h index a6317664..e7563b84 100644 --- a/file.h +++ b/file.h @@ -97,6 +97,13 @@ struct fio_file { uint64_t first_write; uint64_t last_write; + /* + * Tracks the last iodepth number of completed writes, if data + * verification is enabled + */ + uint64_t *last_write_comp; + unsigned int last_write_idx; + /* * For use by the io engine */ diff --git a/fio.h b/fio.h index 30fbde06..829cc818 100644 --- a/fio.h +++ b/fio.h @@ -154,13 +154,6 @@ struct thread_data { uint64_t stat_io_blocks[DDIR_RWDIR_CNT]; struct timeval iops_sample_time; - /* - * Tracks the last iodepth number of completed writes, if data - * verification is enabled - */ - uint64_t *last_write_comp; - unsigned int last_write_idx; - volatile int update_rusage; struct fio_mutex *rusage_sem; struct rusage ru_start; diff --git a/io_u.c b/io_u.c index ea08c927..2adcbd7f 100644 --- a/io_u.c +++ b/io_u.c @@ -1735,6 +1735,25 @@ static void account_io_completion(struct thread_data *td, struct io_u *io_u, } } +static void file_log_write_comp(const struct thread_data *td, struct fio_file *f, + uint64_t offset, unsigned int bytes) +{ + int idx; + + if (f->first_write == -1ULL || offset < f->first_write) + f->first_write = offset; + if (f->last_write == -1ULL || ((offset + bytes) > f->last_write)) + f->last_write = offset + bytes; + + if (!f->last_write_comp) + return; + + idx = f->last_write_idx++; + f->last_write_comp[idx] = offset; + if (f->last_write_idx == td->o.iodepth) + f->last_write_idx = 0; +} + static void io_completed(struct thread_data *td, struct io_u **io_u_ptr, struct io_completion_data *icd) { @@ -1785,23 +1804,8 @@ static void io_completed(struct thread_data *td, struct io_u **io_u_ptr, if (!(io_u->flags & IO_U_F_VER_LIST)) td->this_io_bytes[ddir] += bytes; - if (ddir == DDIR_WRITE) { - if (f) { - if (f->first_write == -1ULL || - io_u->offset < f->first_write) - f->first_write = io_u->offset; - if (f->last_write == -1ULL || - ((io_u->offset + bytes) > f->last_write)) - f->last_write = io_u->offset + bytes; - } - if (td->last_write_comp) { - int idx = td->last_write_idx++; - - td->last_write_comp[idx] = io_u->offset; - if (td->last_write_idx == td->o.iodepth) - td->last_write_idx = 0; - } - } + if (ddir == DDIR_WRITE && f) + file_log_write_comp(td, f, io_u->offset, bytes); if (ramp_time_over(td) && (td->runstate == TD_RUNNING || td->runstate == TD_VERIFYING)) diff --git a/server.c b/server.c index 6416a5c6..dcb7c2da 100644 --- a/server.c +++ b/server.c @@ -1819,7 +1819,7 @@ void fio_server_send_start(struct thread_data *td) } int fio_server_get_verify_state(const char *name, int threadnumber, - void **datap, int *version) + void **datap) { struct thread_io_list *s; struct cmd_sendfile out; @@ -1871,7 +1871,7 @@ fail: * the header, and the thread_io_list checksum */ s = rep->data + sizeof(struct verify_state_hdr); - if (verify_state_hdr(rep->data, s, version)) { + if (verify_state_hdr(rep->data, s)) { ret = EILSEQ; goto fail; } diff --git a/server.h b/server.h index fd0a0ce3..7fc3ec6c 100644 --- a/server.h +++ b/server.h @@ -211,7 +211,7 @@ extern void fio_server_send_ts(struct thread_stat *, struct group_run_stats *); extern void fio_server_send_gs(struct group_run_stats *); extern void fio_server_send_du(void); extern void fio_server_send_job_options(struct flist_head *, unsigned int); -extern int fio_server_get_verify_state(const char *, int, void **, int *); +extern int fio_server_get_verify_state(const char *, int, void **); extern struct fio_net_cmd *fio_net_recv_cmd(int sk, bool wait); diff --git a/t/verify-state.c b/t/verify-state.c index 6e8cd354..0872e29d 100644 --- a/t/verify-state.c +++ b/t/verify-state.c @@ -26,8 +26,37 @@ static void show_s(struct thread_io_list *s, unsigned int no_s) printf("Index: %llu\n", (unsigned long long) s->index); printf("Completions:\n"); - for (i = 0; i < s->no_comps; i++) - printf("\t%llu\n", (unsigned long long) s->offsets[i]); + for (i = 0; i < s->no_comps; i++) { + printf("\t(file=%2llu) %llu\n", + (unsigned long long) s->comps[i].fileno, + (unsigned long long) s->comps[i].offset); + } +} + +static void show(struct thread_io_list *s, size_t size) +{ + int no_s; + + no_s = 0; + do { + int i; + + s->no_comps = le64_to_cpu(s->no_comps); + s->depth = le32_to_cpu(s->depth); + s->nofiles = le32_to_cpu(s->nofiles); + s->numberio = le64_to_cpu(s->numberio); + s->index = le64_to_cpu(s->index); + + for (i = 0; i < s->no_comps; i++) { + s->comps[i].fileno = le64_to_cpu(s->comps[i].fileno); + s->comps[i].offset = le64_to_cpu(s->comps[i].offset); + } + + show_s(s, no_s); + no_s++; + size -= __thread_io_list_sz(s->depth, s->nofiles); + s = (void *) s + __thread_io_list_sz(s->depth, s->nofiles); + } while (size != 0); } static void show_verify_state(void *buf, size_t size) @@ -35,7 +64,6 @@ static void show_verify_state(void *buf, size_t size) struct verify_state_hdr *hdr = buf; struct thread_io_list *s; uint32_t crc; - int no_s; hdr->version = le64_to_cpu(hdr->version); hdr->size = le64_to_cpu(hdr->size); @@ -58,28 +86,10 @@ static void show_verify_state(void *buf, size_t size) return; } - if (hdr->version != 0x02) { - log_err("Can only handle version 2 headers\n"); - return; - } - - no_s = 0; - do { - int i; - - s->no_comps = le64_to_cpu(s->no_comps); - s->depth = le64_to_cpu(s->depth); - s->numberio = le64_to_cpu(s->numberio); - s->index = le64_to_cpu(s->index); - - for (i = 0; i < s->no_comps; i++) - s->offsets[i] = le64_to_cpu(s->offsets[i]); - - show_s(s, no_s); - no_s++; - size -= __thread_io_list_sz(s->depth); - s = (void *) s + __thread_io_list_sz(s->depth); - } while (size != 0); + if (hdr->version == 0x03) + show(s, size); + else + log_err("Unsupported version %d\n", (int) hdr->version); } int main(int argc, char *argv[]) diff --git a/verify-state.h b/verify-state.h index 0d004b0e..f1dc069e 100644 --- a/verify-state.h +++ b/verify-state.h @@ -22,24 +22,20 @@ struct thread_rand_state { /* * For dumping current write state */ -struct thread_io_list { - uint64_t no_comps; - uint64_t depth; - uint64_t numberio; - uint64_t index; - struct thread_rand_state rand; - uint8_t name[64]; - uint64_t offsets[0]; +struct file_comp { + uint64_t fileno; + uint64_t offset; }; -struct thread_io_list_v1 { +struct thread_io_list { uint64_t no_comps; - uint64_t depth; + uint32_t depth; + uint32_t nofiles; uint64_t numberio; uint64_t index; - struct thread_rand32_state rand; + struct thread_rand_state rand; uint8_t name[64]; - uint64_t offsets[0]; + struct file_comp comps[0]; }; struct all_io_list { @@ -47,8 +43,7 @@ struct all_io_list { struct thread_io_list state[0]; }; -#define VSTATE_HDR_VERSION_V1 0x01 -#define VSTATE_HDR_VERSION 0x02 +#define VSTATE_HDR_VERSION 0x03 struct verify_state_hdr { uint64_t version; @@ -65,18 +60,17 @@ extern void verify_save_state(int mask); extern int verify_load_state(struct thread_data *, const char *); extern void verify_free_state(struct thread_data *); extern int verify_state_should_stop(struct thread_data *, struct io_u *); -extern void verify_convert_assign_state(struct thread_data *, void *, int); -extern int verify_state_hdr(struct verify_state_hdr *, struct thread_io_list *, - int *); +extern void verify_assign_state(struct thread_data *, void *); +extern int verify_state_hdr(struct verify_state_hdr *, struct thread_io_list *); -static inline size_t __thread_io_list_sz(uint64_t depth) +static inline size_t __thread_io_list_sz(uint32_t depth, uint32_t nofiles) { - return sizeof(struct thread_io_list) + depth * sizeof(uint64_t); + return sizeof(struct thread_io_list) + depth * nofiles * sizeof(struct file_comp); } static inline size_t thread_io_list_sz(struct thread_io_list *s) { - return __thread_io_list_sz(le64_to_cpu(s->depth)); + return __thread_io_list_sz(le32_to_cpu(s->depth), le32_to_cpu(s->nofiles)); } static inline struct thread_io_list *io_list_next(struct thread_io_list *s) diff --git a/verify.c b/verify.c index 0f43a3e0..838db10d 100644 --- a/verify.c +++ b/verify.c @@ -1353,6 +1353,47 @@ int paste_blockoff(char *buf, unsigned int len, void *priv) return 0; } +static int __fill_file_completions(struct thread_data *td, + struct thread_io_list *s, + struct fio_file *f, unsigned int *index) +{ + unsigned int comps; + int i, j; + + if (!f->last_write_comp) + return 0; + + if (td->io_blocks[DDIR_WRITE] < td->o.iodepth) + comps = td->io_blocks[DDIR_WRITE]; + else + comps = td->o.iodepth; + + j = f->last_write_idx - 1; + for (i = 0; i < comps; i++) { + if (j == -1) + j = td->o.iodepth - 1; + s->comps[*index].fileno = __cpu_to_le64(f->fileno); + s->comps[*index].offset = cpu_to_le64(f->last_write_comp[j]); + (*index)++; + j--; + } + + return comps; +} + +static int fill_file_completions(struct thread_data *td, + struct thread_io_list *s, unsigned int *index) +{ + struct fio_file *f; + unsigned int i; + int comps = 0; + + for_each_file(td, f, i) + comps += __fill_file_completions(td, s, f, index); + + return comps; +} + struct all_io_list *get_all_io_list(int save_mask, size_t *sz) { struct all_io_list *rep; @@ -1374,7 +1415,7 @@ struct all_io_list *get_all_io_list(int save_mask, size_t *sz) continue; td->stop_io = 1; td->flags |= TD_F_VSTATE_SAVED; - depth += td->o.iodepth; + depth += (td->o.iodepth * td->o.nr_files); nr++; } @@ -1383,7 +1424,7 @@ struct all_io_list *get_all_io_list(int save_mask, size_t *sz) *sz = sizeof(*rep); *sz += nr * sizeof(struct thread_io_list); - *sz += depth * sizeof(uint64_t); + *sz += depth * sizeof(struct file_comp); rep = malloc(*sz); memset(rep, 0, *sz); @@ -1392,31 +1433,16 @@ struct all_io_list *get_all_io_list(int save_mask, size_t *sz) next = &rep->state[0]; for_each_td(td, i) { struct thread_io_list *s = next; - unsigned int comps; + unsigned int comps, index = 0; if (save_mask != IO_LIST_ALL && (i + 1) != save_mask) continue; - if (td->last_write_comp) { - int j, k; - - if (td->io_blocks[DDIR_WRITE] < td->o.iodepth) - comps = td->io_blocks[DDIR_WRITE]; - else - comps = td->o.iodepth; - - k = td->last_write_idx - 1; - for (j = 0; j < comps; j++) { - if (k == -1) - k = td->o.iodepth - 1; - s->offsets[j] = cpu_to_le64(td->last_write_comp[k]); - k--; - } - } else - comps = 0; + comps = fill_file_completions(td, s, &index); s->no_comps = cpu_to_le64((uint64_t) comps); s->depth = cpu_to_le64((uint64_t) td->o.iodepth); + s->nofiles = cpu_to_le64((uint64_t) td->o.nr_files); s->numberio = cpu_to_le64((uint64_t) td->io_issues[DDIR_WRITE]); s->index = cpu_to_le64((uint64_t) i); if (td->random_state.use64) { @@ -1536,72 +1562,34 @@ void verify_free_state(struct thread_data *td) free(td->vstate); } -static struct thread_io_list *convert_v1_list(struct thread_io_list_v1 *s) +void verify_assign_state(struct thread_data *td, void *p) { - struct thread_io_list *til; + struct thread_io_list *s = p; int i; - til = malloc(__thread_io_list_sz(s->no_comps)); - til->no_comps = s->no_comps; - til->depth = s->depth; - til->numberio = s->numberio; - til->index = s->index; - memcpy(til->name, s->name, sizeof(til->name)); - - til->rand.use64 = 0; - for (i = 0; i < 4; i++) - til->rand.state32.s[i] = s->rand.s[i]; + s->no_comps = le64_to_cpu(s->no_comps); + s->depth = le32_to_cpu(s->depth); + s->nofiles = le32_to_cpu(s->nofiles); + s->numberio = le64_to_cpu(s->numberio); + s->rand.use64 = le64_to_cpu(s->rand.use64); - for (i = 0; i < s->no_comps; i++) - til->offsets[i] = s->offsets[i]; - - return til; -} - -void verify_convert_assign_state(struct thread_data *td, void *p, int version) -{ - struct thread_io_list *til; - int i; - - if (version == 1) { - struct thread_io_list_v1 *s = p; - - s->no_comps = le64_to_cpu(s->no_comps); - s->depth = le64_to_cpu(s->depth); - s->numberio = le64_to_cpu(s->numberio); - for (i = 0; i < 4; i++) - s->rand.s[i] = le32_to_cpu(s->rand.s[i]); - for (i = 0; i < s->no_comps; i++) - s->offsets[i] = le64_to_cpu(s->offsets[i]); - - til = convert_v1_list(s); - free(s); + if (s->rand.use64) { + for (i = 0; i < 6; i++) + s->rand.state64.s[i] = le64_to_cpu(s->rand.state64.s[i]); } else { - struct thread_io_list *s = p; - - s->no_comps = le64_to_cpu(s->no_comps); - s->depth = le64_to_cpu(s->depth); - s->numberio = le64_to_cpu(s->numberio); - s->rand.use64 = le64_to_cpu(s->rand.use64); - - if (s->rand.use64) { - for (i = 0; i < 6; i++) - s->rand.state64.s[i] = le64_to_cpu(s->rand.state64.s[i]); - } else { - for (i = 0; i < 4; i++) - s->rand.state32.s[i] = le32_to_cpu(s->rand.state32.s[i]); - } - for (i = 0; i < s->no_comps; i++) - s->offsets[i] = le64_to_cpu(s->offsets[i]); + for (i = 0; i < 4; i++) + s->rand.state32.s[i] = le32_to_cpu(s->rand.state32.s[i]); + } - til = p; + for (i = 0; i < s->no_comps; i++) { + s->comps[i].fileno = le64_to_cpu(s->comps[i].fileno); + s->comps[i].offset = le64_to_cpu(s->comps[i].offset); } - td->vstate = til; + td->vstate = p; } -int verify_state_hdr(struct verify_state_hdr *hdr, struct thread_io_list *s, - int *version) +int verify_state_hdr(struct verify_state_hdr *hdr, struct thread_io_list *s) { uint64_t crc; @@ -1609,15 +1597,13 @@ int verify_state_hdr(struct verify_state_hdr *hdr, struct thread_io_list *s, hdr->size = le64_to_cpu(hdr->size); hdr->crc = le64_to_cpu(hdr->crc); - if (hdr->version != VSTATE_HDR_VERSION && - hdr->version != VSTATE_HDR_VERSION_V1) + if (hdr->version != VSTATE_HDR_VERSION) return 1; crc = fio_crc32c((void *)s, hdr->size); if (crc != hdr->crc) return 1; - *version = hdr->version; return 0; } @@ -1648,9 +1634,9 @@ int verify_load_state(struct thread_data *td, const char *prefix) hdr.size = le64_to_cpu(hdr.size); hdr.crc = le64_to_cpu(hdr.crc); - if (hdr.version != VSTATE_HDR_VERSION && - hdr.version != VSTATE_HDR_VERSION_V1) { - log_err("fio: bad version in verify state header\n"); + if (hdr.version != VSTATE_HDR_VERSION) { + log_err("fio: unsupported (%d) version in verify state header\n", + (unsigned int) hdr.version); goto err; } @@ -1671,7 +1657,7 @@ int verify_load_state(struct thread_data *td, const char *prefix) close(fd); - verify_convert_assign_state(td, s, hdr.version); + verify_assign_state(td, s); return 0; err: if (s) @@ -1686,9 +1672,10 @@ err: int verify_state_should_stop(struct thread_data *td, struct io_u *io_u) { struct thread_io_list *s = td->vstate; + struct fio_file *f = io_u->file; int i; - if (!s) + if (!s || !f) return 0; /* @@ -1705,9 +1692,12 @@ int verify_state_should_stop(struct thread_data *td, struct io_u *io_u) * completed or not. If the IO was seen as completed, then * lets verify it. */ - for (i = 0; i < s->no_comps; i++) - if (io_u->offset == s->offsets[i]) + for (i = 0; i < s->no_comps; i++) { + if (s->comps[i].fileno != f->fileno) + continue; + if (io_u->offset == s->comps[i].offset) return 0; + } /* * Not found, we have to stop