[PATCH 5/9] Added the changes for copy operation support in FIO.

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



From: Ankit Kumar <ankit.kumar@xxxxxxxxxxx>

The source ranges for copy operation uses the existing offset generation
algorithm. As each copy operation has num_range source ranges
the get_next_offset will be called that many times. The data buffer
will now contain a range list, with each entry having a start offset
and the length in bytes for that source range.
For generating the destination offset added a new function.
Each successful copy operation will copy 'num_range' * 'block_size'
amount of data.

Signed-off-by: Krishna Kanth Reddy <krish.reddy@xxxxxxxxxxx>
---
 backend.c     |  22 +++++++++--
 file.h        |   3 ++
 filesetup.c   |  61 ++++++++++++++++++++++++++++++
 fio.h         |  12 ++++++
 init.c        |  39 +++++++++++--------
 io_u.c        | 103 +++++++++++++++++++++++++++++++++++++++++---------
 rate-submit.c |   2 +
 zbd.c         |   2 +
 8 files changed, 207 insertions(+), 37 deletions(-)

diff --git a/backend.c b/backend.c
index 2f4d6ac4..c0569c59 100644
--- a/backend.c
+++ b/backend.c
@@ -521,8 +521,13 @@ sync_done:
 		 */
 		if (td->io_ops->commit == NULL)
 			io_u_queued(td, io_u);
-		if (bytes_issued)
-			*bytes_issued += io_u->xfer_buflen;
+		if (bytes_issued) {
+			if (io_u->ddir == DDIR_COPY) {
+				*bytes_issued += (((io_u->xfer_buflen) * td->o.bs[DDIR_COPY]) /
+						   sizeof(struct source_range));
+			} else
+				*bytes_issued += io_u->xfer_buflen;
+		}
 		break;
 	case FIO_Q_BUSY:
 		if (!from_verify)
@@ -721,6 +726,10 @@ static void do_verify(struct thread_data *td, uint64_t verify_bytes)
 					io_u->ddir = DDIR_READ;
 					populate_verify_io_u(td, io_u);
 					break;
+				} else if (io_u->ddir == DDIR_COPY) {
+					td->io_issues[DDIR_COPY]++;
+					put_io_u(td, io_u);
+					continue;
 				} else {
 					put_io_u(td, io_u);
 					continue;
@@ -802,6 +811,8 @@ static bool io_bytes_exceeded(struct thread_data *td, uint64_t *this_bytes)
 		bytes = this_bytes[DDIR_WRITE];
 	else if (td_read(td))
 		bytes = this_bytes[DDIR_READ];
+	else if (td_copy(td))
+		bytes = this_bytes[DDIR_COPY];
 	else
 		bytes = this_bytes[DDIR_TRIM];
 
@@ -1278,7 +1289,8 @@ int init_io_u_buffers(struct thread_data *td)
 	td->orig_buffer_size = (unsigned long long) max_bs
 					* (unsigned long long) max_units;
 
-	if (td_ioengine_flagged(td, FIO_NOIO) || !(td_read(td) || td_write(td)))
+	if (td_ioengine_flagged(td, FIO_NOIO) || !(td_read(td) ||
+	    td_write(td) || td_copy(td)))
 		data_xfer = 0;
 
 	/*
@@ -1751,13 +1763,15 @@ static void *thread_main(void *data)
 	memcpy(&td->ss.prev_time, &td->epoch, sizeof(td->epoch));
 
 	if (o->ratemin[DDIR_READ] || o->ratemin[DDIR_WRITE] ||
-			o->ratemin[DDIR_TRIM]) {
+	    o->ratemin[DDIR_TRIM] || o->ratemin[DDIR_COPY]) {
 	        memcpy(&td->lastrate[DDIR_READ], &td->bw_sample_time,
 					sizeof(td->bw_sample_time));
 	        memcpy(&td->lastrate[DDIR_WRITE], &td->bw_sample_time,
 					sizeof(td->bw_sample_time));
 	        memcpy(&td->lastrate[DDIR_TRIM], &td->bw_sample_time,
 					sizeof(td->bw_sample_time));
+	        memcpy(&td->lastrate[DDIR_COPY], &td->bw_sample_time,
+					sizeof(td->bw_sample_time));
 	}
 
 	memset(bytes_done, 0, sizeof(bytes_done));
diff --git a/file.h b/file.h
index 493ec04a..f5a794e4 100644
--- a/file.h
+++ b/file.h
@@ -99,6 +99,7 @@ struct fio_file {
 	 */
 	uint64_t real_file_size;
 	uint64_t file_offset;
+	uint64_t file_dest_offset;
 	uint64_t io_size;
 
 	/*
@@ -113,6 +114,7 @@ struct fio_file {
 	 * Track last end and last start of IO for a given data direction
 	 */
 	uint64_t last_pos[DDIR_RWDIR_CNT];
+	uint64_t last_pos_dest[DDIR_RWDIR_CNT];
 	uint64_t last_start[DDIR_RWDIR_CNT];
 
 	uint64_t first_write;
@@ -199,6 +201,7 @@ struct thread_data;
 extern void close_files(struct thread_data *);
 extern void close_and_free_files(struct thread_data *);
 extern uint64_t get_start_offset(struct thread_data *, struct fio_file *);
+extern uint64_t get_dest_offset(struct thread_data *, struct fio_file *);
 extern int __must_check setup_files(struct thread_data *);
 extern int __must_check file_invalidate_cache(struct thread_data *, struct fio_file *);
 #ifdef __cplusplus
diff --git a/filesetup.c b/filesetup.c
index 42c5f630..68a21fac 100644
--- a/filesetup.c
+++ b/filesetup.c
@@ -679,6 +679,14 @@ open_again:
 		else
 			flags |= O_RDONLY;
 
+		if (is_std)
+			f->fd = dup(STDIN_FILENO);
+		else
+			from_hash = file_lookup_open(f, flags);
+	} else if (td_copy(td)) {
+		if (!read_only)
+			flags |= O_RDWR;
+
 		if (is_std)
 			f->fd = dup(STDIN_FILENO);
 		else
@@ -911,6 +919,54 @@ uint64_t get_start_offset(struct thread_data *td, struct fio_file *f)
 	return offset;
 }
 
+uint64_t get_dest_offset(struct thread_data *td, struct fio_file *f)
+{
+	bool align = false;
+	struct thread_options *o = &td->o;
+	unsigned long long align_bs;
+	unsigned long long offset;
+	unsigned long long increment;
+
+	if (o->offset_increment_percent) {
+		assert(!o->offset_increment);
+		increment = o->offset_increment_percent * f->real_file_size / 100;
+		align = true;
+	} else
+		increment = o->offset_increment;
+
+	if (o->dest_offset_percent > 0) {
+		/* calculate the raw offset */
+		offset = (f->real_file_size * o->dest_offset_percent / 100) +
+			(td->subjob_number * increment);
+
+		align = true;
+	} else {
+		/* start_offset_percent not set */
+		offset = o->dest_offset +
+				td->subjob_number * increment;
+	}
+
+	if (align) {
+		/*
+		 * if offset_align is provided, use it
+		 */
+		if (fio_option_is_set(o, start_offset_align)) {
+			align_bs = o->start_offset_align;
+		} else {
+			/* else take the minimum block size */
+			align_bs = td_min_bs(td);
+		}
+
+		/*
+		 * block align the offset at the next available boundary at
+		 * ceiling(offset / align_bs) * align_bs
+		 */
+		offset = (offset / align_bs + (offset % align_bs != 0)) * align_bs;
+	}
+
+	return offset;
+}
+
 /*
  * Find longest path component that exists and return its length
  */
@@ -1172,6 +1228,9 @@ int setup_files(struct thread_data *td)
 				    td_ioengine_flagged(td, FIO_FAKEIO)))
 				f->real_file_size = f->io_size + f->file_offset;
 		}
+
+		if (td_copy(td))
+			f->file_dest_offset = get_dest_offset(td, f);
 	}
 
 	if (td->o.block_error_hist) {
@@ -1310,6 +1369,7 @@ static void __init_rand_distribution(struct thread_data *td, struct fio_file *f)
 	uint64_t fsize;
 
 	range_size = min(td->o.min_bs[DDIR_READ], td->o.min_bs[DDIR_WRITE]);
+	range_size = min((unsigned long long)range_size, td->o.min_bs[DDIR_COPY]);
 	fsize = min(f->real_file_size, f->io_size);
 
 	nranges = (fsize + range_size - 1ULL) / range_size;
@@ -1956,6 +2016,7 @@ void fio_file_reset(struct thread_data *td, struct fio_file *f)
 	for (i = 0; i < DDIR_RWDIR_CNT; i++) {
 		f->last_pos[i] = f->file_offset;
 		f->last_start[i] = -1ULL;
+		f->last_pos_dest[i] = f->file_dest_offset;
 	}
 
 	if (fio_file_axmap(f))
diff --git a/fio.h b/fio.h
index fffec001..5c1a7c88 100644
--- a/fio.h
+++ b/fio.h
@@ -70,6 +70,14 @@
 
 struct fio_sem;
 
+/*
+ * Source range data for copy command
+ */
+struct source_range {
+       uint64_t  start;
+       uint64_t  len;
+};
+
 /*
  * offset generator types
  */
@@ -123,6 +131,7 @@ enum {
 	FIO_RAND_BS_OFF		= 0,
 	FIO_RAND_BS1_OFF,
 	FIO_RAND_BS2_OFF,
+	FIO_RAND_BS3_OFF,
 	FIO_RAND_VER_OFF,
 	FIO_RAND_MIX_OFF,
 	FIO_RAND_FILE_OFF,
@@ -133,6 +142,7 @@ enum {
 	FIO_RAND_SEQ_RAND_READ_OFF,
 	FIO_RAND_SEQ_RAND_WRITE_OFF,
 	FIO_RAND_SEQ_RAND_TRIM_OFF,
+	FIO_RAND_SEQ_RAND_COPY_OFF,
 	FIO_RAND_START_DELAY,
 	FIO_DEDUPE_OFF,
 	FIO_RAND_POISSON_OFF,
@@ -774,6 +784,7 @@ static inline unsigned long long td_max_bs(struct thread_data *td)
 	unsigned long long max_bs;
 
 	max_bs = max(td->o.max_bs[DDIR_READ], td->o.max_bs[DDIR_WRITE]);
+	max_bs = max(td->o.max_bs[DDIR_COPY], max_bs);
 	return max(td->o.max_bs[DDIR_TRIM], max_bs);
 }
 
@@ -782,6 +793,7 @@ static inline unsigned long long td_min_bs(struct thread_data *td)
 	unsigned long long min_bs;
 
 	min_bs = min(td->o.min_bs[DDIR_READ], td->o.min_bs[DDIR_WRITE]);
+	min_bs = min(td->o.min_bs[DDIR_COPY], min_bs);
 	return min(td->o.min_bs[DDIR_TRIM], min_bs);
 }
 
diff --git a/init.c b/init.c
index f9c20bdb..e5835b7b 100644
--- a/init.c
+++ b/init.c
@@ -592,8 +592,10 @@ static int fixed_block_size(struct thread_options *o)
 	return o->min_bs[DDIR_READ] == o->max_bs[DDIR_READ] &&
 		o->min_bs[DDIR_WRITE] == o->max_bs[DDIR_WRITE] &&
 		o->min_bs[DDIR_TRIM] == o->max_bs[DDIR_TRIM] &&
+		o->min_bs[DDIR_COPY] == o->max_bs[DDIR_COPY] &&
 		o->min_bs[DDIR_READ] == o->min_bs[DDIR_WRITE] &&
-		o->min_bs[DDIR_READ] == o->min_bs[DDIR_TRIM];
+		o->min_bs[DDIR_READ] == o->min_bs[DDIR_TRIM] &&
+		o->min_bs[DDIR_READ] == o->min_bs[DDIR_COPY];
 }
 
 /*
@@ -616,8 +618,8 @@ static int fixup_options(struct thread_data *td)
 	struct thread_options *o = &td->o;
 	int ret = 0;
 
-	if (read_only && (td_write(td) || td_trim(td))) {
-		log_err("fio: trim and write operations are not allowed"
+	if (read_only && (td_write(td) || td_trim(td) || td_copy(td))) {
+		log_err("fio: trim, copy and write operations are not allowed"
 			 " with the --readonly parameter.\n");
 		ret |= 1;
 	}
@@ -670,9 +672,9 @@ static int fixup_options(struct thread_data *td)
 		o->zone_range = o->zone_size;
 
 	/*
-	 * Reads can do overwrites, we always need to pre-create the file
+	 * Reads and copies can do overwrites, we always need to pre-create the file
 	 */
-	if (td_read(td))
+	if (td_read(td) && td_copy(td))
 		o->overwrite = 1;
 
 	for_each_rw_ddir(ddir) {
@@ -697,7 +699,8 @@ static int fixup_options(struct thread_data *td)
 
 	if ((o->ba[DDIR_READ] != o->min_bs[DDIR_READ] ||
 	    o->ba[DDIR_WRITE] != o->min_bs[DDIR_WRITE] ||
-	    o->ba[DDIR_TRIM] != o->min_bs[DDIR_TRIM]) &&
+	    o->ba[DDIR_TRIM] != o->min_bs[DDIR_TRIM] ||
+	    o->ba[DDIR_COPY] != o->min_bs[DDIR_COPY]) &&
 	    !o->norandommap) {
 		log_err("fio: Any use of blockalign= turns off randommap\n");
 		o->norandommap = 1;
@@ -765,10 +768,10 @@ static int fixup_options(struct thread_data *td)
 	if (o->open_files > o->nr_files || !o->open_files)
 		o->open_files = o->nr_files;
 
-	if (((o->rate[DDIR_READ] + o->rate[DDIR_WRITE] + o->rate[DDIR_TRIM]) &&
-	    (o->rate_iops[DDIR_READ] + o->rate_iops[DDIR_WRITE] + o->rate_iops[DDIR_TRIM])) ||
-	    ((o->ratemin[DDIR_READ] + o->ratemin[DDIR_WRITE] + o->ratemin[DDIR_TRIM]) &&
-	    (o->rate_iops_min[DDIR_READ] + o->rate_iops_min[DDIR_WRITE] + o->rate_iops_min[DDIR_TRIM]))) {
+	if (((o->rate[DDIR_READ] + o->rate[DDIR_WRITE] + o->rate[DDIR_TRIM] + o->rate[DDIR_COPY]) &&
+	    (o->rate_iops[DDIR_READ] + o->rate_iops[DDIR_WRITE] + o->rate_iops[DDIR_TRIM] + o->rate_iops[DDIR_COPY])) ||
+	    ((o->ratemin[DDIR_READ] + o->ratemin[DDIR_WRITE] + o->ratemin[DDIR_TRIM] + o->ratemin[DDIR_COPY]) &&
+	    (o->rate_iops_min[DDIR_READ] + o->rate_iops_min[DDIR_WRITE] + o->rate_iops_min[DDIR_TRIM] + o->rate_iops_min[DDIR_COPY]))) {
 		log_err("fio: rate and rate_iops are mutually exclusive\n");
 		ret |= 1;
 	}
@@ -1000,6 +1003,7 @@ static void td_fill_rand_seeds_internal(struct thread_data *td, bool use64)
 	uint64_t read_seed = td->rand_seeds[FIO_RAND_BS_OFF];
 	uint64_t write_seed = td->rand_seeds[FIO_RAND_BS1_OFF];
 	uint64_t trim_seed = td->rand_seeds[FIO_RAND_BS2_OFF];
+	uint64_t copy_seed = td->rand_seeds[FIO_RAND_BS3_OFF];
 	int i;
 
 	/*
@@ -1017,6 +1021,7 @@ static void td_fill_rand_seeds_internal(struct thread_data *td, bool use64)
 	init_rand_seed(&td->bsrange_state[DDIR_READ], read_seed, use64);
 	init_rand_seed(&td->bsrange_state[DDIR_WRITE], write_seed, use64);
 	init_rand_seed(&td->bsrange_state[DDIR_TRIM], trim_seed, use64);
+	init_rand_seed(&td->bsrange_state[DDIR_COPY], copy_seed, use64);
 
 	td_fill_verify_state_seed(td);
 	init_rand_seed(&td->rwmix_state, td->rand_seeds[FIO_RAND_MIX_OFF], false);
@@ -1675,7 +1680,7 @@ static int add_job(struct thread_data *td, const char *jobname, int job_add_num,
 				fio_server_send_add_job(td);
 
 			if (!td_ioengine_flagged(td, FIO_NOIO)) {
-				char *c1, *c2, *c3, *c4;
+				char *c1, *c2, *c3, *c4, *c7, *c8;
 				char *c5 = NULL, *c6 = NULL;
 				int i2p = is_power_of_2(o->kb_base);
 				struct buf_output out;
@@ -1684,6 +1689,8 @@ static int add_job(struct thread_data *td, const char *jobname, int job_add_num,
 				c2 = num2str(o->max_bs[DDIR_READ], o->sig_figs, 1, i2p, N2S_BYTE);
 				c3 = num2str(o->min_bs[DDIR_WRITE], o->sig_figs, 1, i2p, N2S_BYTE);
 				c4 = num2str(o->max_bs[DDIR_WRITE], o->sig_figs, 1, i2p, N2S_BYTE);
+				c7 = num2str(o->min_bs[DDIR_COPY], o->sig_figs, 1, i2p, N2S_BYTE);
+				c8 = num2str(o->max_bs[DDIR_COPY], o->sig_figs, 1, i2p, N2S_BYTE);
 
 				if (!o->bs_is_seq_rand) {
 					c5 = num2str(o->min_bs[DDIR_TRIM], o->sig_figs, 1, i2p, N2S_BYTE);
@@ -1696,11 +1703,11 @@ static int add_job(struct thread_data *td, const char *jobname, int job_add_num,
 							ddir_str(o->td_ddir));
 
 				if (o->bs_is_seq_rand)
-					__log_buf(&out, "bs=(R) %s-%s, (W) %s-%s, bs_is_seq_rand, ",
-							c1, c2, c3, c4);
+					__log_buf(&out, "bs=(R) %s-%s, (W) %s-%s, (C) %s-%s, bs_is_seq_rand, ",
+							c1, c2, c3, c4, c7, c8);
 				else
-					__log_buf(&out, "bs=(R) %s-%s, (W) %s-%s, (T) %s-%s, ",
-							c1, c2, c3, c4, c5, c6);
+					__log_buf(&out, "bs=(R) %s-%s, (W) %s-%s, (T) %s-%s, (C) %s-%s, ",
+							c1, c2, c3, c4, c5, c6, c7, c8);
 
 				__log_buf(&out, "ioengine=%s, iodepth=%u\n",
 						td->io_ops->name, o->iodepth);
@@ -1713,6 +1720,8 @@ static int add_job(struct thread_data *td, const char *jobname, int job_add_num,
 				free(c4);
 				free(c5);
 				free(c6);
+				free(c7);
+				free(c8);
 			}
 		} else if (job_add_num == 1)
 			log_info("...\n");
diff --git a/io_u.c b/io_u.c
index f30fc037..83c7960a 100644
--- a/io_u.c
+++ b/io_u.c
@@ -405,6 +405,29 @@ static int get_next_seq_offset(struct thread_data *td, struct fio_file *f,
 	return 1;
 }
 
+static void get_next_dest_seq_offset(struct thread_data *td, struct fio_file *f,
+				     enum fio_ddir ddir, uint64_t num_range,
+				     uint64_t *offset)
+{
+	struct thread_options *o = &td->o;
+
+	assert(ddir_rw(ddir));
+
+	if (f->last_pos_dest[ddir] >= f->io_size + f->file_dest_offset &&
+	    o->time_based) {
+		f->last_pos_dest[ddir] =  f->file_dest_offset;
+		loop_cache_invalidate(td, f);
+	}
+	*offset = f->last_pos_dest[ddir];
+	if (f->last_pos_dest[ddir] >= f->real_file_size)
+		f->last_pos_dest[ddir] = f->file_dest_offset;
+	else {
+		f->last_pos_dest[ddir] += (num_range) * (td->o.bs[ddir]);
+		if (f->last_pos_dest[ddir] >= f->real_file_size)
+			f->last_pos_dest[ddir] =  f->file_dest_offset;
+	}
+}
+
 static int get_next_block(struct thread_data *td, struct io_u *io_u,
 			  enum fio_ddir ddir, int rw_seq,
 			  bool *is_random)
@@ -752,6 +775,8 @@ static enum fio_ddir get_rw_ddir(struct thread_data *td)
 		ddir = DDIR_WRITE;
 	else if (td_trim(td))
 		ddir = DDIR_TRIM;
+	else if (td_copy(td))
+		ddir = DDIR_COPY;
 	else
 		ddir = DDIR_INVAL;
 
@@ -905,8 +930,12 @@ static void setup_strided_zone_mode(struct thread_data *td, struct io_u *io_u)
 static int fill_io_u(struct thread_data *td, struct io_u *io_u)
 {
 	bool is_random;
-	uint64_t offset;
+	uint64_t offset, dest_offset, i = 0;
 	enum io_u_action ret;
+	struct fio_file *f = io_u->file;
+	enum fio_ddir ddir = io_u->ddir;
+	uint8_t *buf_point;
+	struct source_range entry;
 
 	if (td_ioengine_flagged(td, FIO_NOIO))
 		goto out;
@@ -928,22 +957,52 @@ static int fill_io_u(struct thread_data *td, struct io_u *io_u)
 	else if (td->o.zone_mode == ZONE_MODE_ZBD)
 		setup_zbd_zone_mode(td, io_u);
 
-	/*
-	 * No log, let the seq/rand engine retrieve the next buflen and
-	 * position.
-	 */
-	if (get_next_offset(td, io_u, &is_random)) {
-		dprint(FD_IO, "io_u %p, failed getting offset\n", io_u);
-		return 1;
-	}
+	if (io_u->ddir == DDIR_COPY) {
+		buf_point = io_u->buf;
+		offset = 0;
 
-	io_u->buflen = get_next_buflen(td, io_u, is_random);
-	if (!io_u->buflen) {
-		dprint(FD_IO, "io_u %p, failed getting buflen\n", io_u);
-		return 1;
+		while (i < td->o.num_range) {
+			if (get_next_offset(td, io_u, &is_random)) {
+				dprint(FD_IO, "io_u %p, failed getting offset\n",
+				       io_u);
+				return 1;
+			}
+
+			offset = io_u->offset;
+			entry.start = offset;
+			entry.len = td->o.bs[ddir];
+			memcpy(buf_point, &entry, sizeof(struct source_range));
+			buf_point += sizeof(struct source_range);
+			f->last_start[io_u->ddir] = io_u->offset;
+			f->last_pos[io_u->ddir] = io_u->offset + entry.len;
+			i++;
+
+			if (td_random(td) && file_randommap(td, io_u->file))
+				mark_random_map(td, io_u, offset, td->o.bs[ddir]);
+		}
+		get_next_dest_seq_offset(td, f, io_u->ddir, td->o.num_range, &dest_offset);
+		io_u->offset = dest_offset;
+
+		io_u->buflen = i * sizeof(struct source_range);
+	} else {
+		/*
+		 * No log, let the seq/rand engine retrieve the next buflen and
+		 * position.
+		 */
+		if (get_next_offset(td, io_u, &is_random)) {
+			dprint(FD_IO, "io_u %p, failed getting offset\n", io_u);
+			return 1;
+		}
+
+		io_u->buflen = get_next_buflen(td, io_u, is_random);
+		if (!io_u->buflen) {
+			dprint(FD_IO, "io_u %p, failed getting buflen\n", io_u);
+			return 1;
+		}
+
+		offset = io_u->offset;
 	}
 
-	offset = io_u->offset;
 	if (td->o.zone_mode == ZONE_MODE_ZBD) {
 		ret = zbd_adjust_block(td, io_u);
 		if (ret == io_u_eof)
@@ -961,13 +1020,16 @@ static int fill_io_u(struct thread_data *td, struct io_u *io_u)
 	/*
 	 * mark entry before potentially trimming io_u
 	 */
-	if (td_random(td) && file_randommap(td, io_u->file))
+	if (!(io_u->ddir == DDIR_COPY) && td_random(td) && file_randommap(td, io_u->file))
 		io_u->buflen = mark_random_map(td, io_u, offset, io_u->buflen);
 
 out:
 	dprint_io_u(io_u, "fill");
 	io_u->verify_offset = io_u->offset;
-	td->zone_bytes += io_u->buflen;
+	if (!(io_u->ddir == DDIR_COPY))
+		td->zone_bytes += io_u->buflen;
+	else
+		td->zone_bytes += (td->o.num_range * td->o.bs[DDIR_COPY]);
 	return 0;
 }
 
@@ -1759,7 +1821,7 @@ struct io_u *get_io_u(struct thread_data *td)
 
 	assert(fio_file_open(f));
 
-	if (ddir_rw(io_u->ddir)) {
+	if (ddir_rw(io_u->ddir) && !(io_u->ddir == DDIR_COPY)) {
 		if (!io_u->buflen && !td_ioengine_flagged(td, FIO_NOIO)) {
 			dprint(FD_IO, "get_io_u: zero buflen on %p\n", io_u);
 			goto err_put;
@@ -1982,9 +2044,14 @@ static void io_completed(struct thread_data *td, struct io_u **io_u_ptr,
 	td->last_ddir = ddir;
 
 	if (!io_u->error && ddir_rw(ddir)) {
-		unsigned long long bytes = io_u->xfer_buflen - io_u->resid;
+		unsigned long long bytes;
 		int ret;
 
+		if (io_u->ddir == DDIR_COPY)
+			bytes = (((io_u->xfer_buflen) * td->o.bs[DDIR_COPY]) /
+						   sizeof(struct source_range));
+		else
+			bytes = io_u->xfer_buflen - io_u->resid;
 		/*
 		 * Make sure we notice short IO from here, and requeue them
 		 * appropriately!
diff --git a/rate-submit.c b/rate-submit.c
index 13dbe7a2..de99906e 100644
--- a/rate-submit.c
+++ b/rate-submit.c
@@ -269,6 +269,8 @@ static void io_workqueue_update_acct_fn(struct submit_worker *sw)
 		sum_ddir(dst, src, DDIR_WRITE);
 	if (td_trim(src))
 		sum_ddir(dst, src, DDIR_TRIM);
+	if (td_copy(src))
+		sum_ddir(dst, src, DDIR_COPY);
 
 }
 
diff --git a/zbd.c b/zbd.c
index 9327816a..58fed98e 100644
--- a/zbd.c
+++ b/zbd.c
@@ -1682,6 +1682,8 @@ enum io_u_action zbd_adjust_block(struct thread_data *td, struct io_u *io_u)
 	case DDIR_LAST:
 	case DDIR_INVAL:
 		goto accept;
+	case DDIR_COPY:
+		goto eof;
 	}
 
 	assert(false);
-- 
2.17.1




[Index of Archives]     [Linux Kernel]     [Linux SCSI]     [Linux IDE]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux SCSI]

  Powered by Linux