Home | History | Annotate | Download | only in engines
      1 /*
      2  * ioe_e4defrag:  ioengine for git://git.kernel.dk/fio.git
      3  *
      4  * IO engine that does regular EXT4_IOC_MOVE_EXT ioctls to simulate
      5  * defragment activity
      6  *
      7  */
      8 
      9 #include <sys/types.h>
     10 #include <sys/stat.h>
     11 #include <stdio.h>
     12 #include <stdlib.h>
     13 #include <unistd.h>
     14 #include <sys/uio.h>
     15 #include <errno.h>
     16 #include <assert.h>
     17 #include <fcntl.h>
     18 
     19 #include "../fio.h"
     20 #include "../optgroup.h"
     21 
     22 #ifndef EXT4_IOC_MOVE_EXT
     23 #define EXT4_IOC_MOVE_EXT               _IOWR('f', 15, struct move_extent)
     24 struct move_extent {
     25 	__u32 reserved;         /* should be zero */
     26 	__u32 donor_fd;         /* donor file descriptor */
     27 	__u64 orig_start;       /* logical start offset in block for orig */
     28 	__u64 donor_start;      /* logical start offset in block for donor */
     29 	__u64 len;              /* block length to be moved */
     30 	__u64 moved_len;        /* moved block length */
     31 };
     32 #endif
     33 
     34 struct e4defrag_data {
     35 	int donor_fd;
     36 	int bsz;
     37 };
     38 
     39 struct e4defrag_options {
     40 	void *pad;
     41 	unsigned int inplace;
     42 	char * donor_name;
     43 };
     44 
     45 static struct fio_option options[] = {
     46 	{
     47 		.name	= "donorname",
     48 		.lname	= "Donor Name",
     49 		.type	= FIO_OPT_STR_STORE,
     50 		.off1	= offsetof(struct e4defrag_options, donor_name),
     51 		.help	= "File used as a block donor",
     52 		.category = FIO_OPT_C_ENGINE,
     53 		.group	= FIO_OPT_G_E4DEFRAG,
     54 	},
     55 	{
     56 		.name	= "inplace",
     57 		.lname	= "In Place",
     58 		.type	= FIO_OPT_INT,
     59 		.off1	= offsetof(struct e4defrag_options, inplace),
     60 		.minval	= 0,
     61 		.maxval	= 1,
     62 		.help	= "Alloc and free space inside defrag event",
     63 		.category = FIO_OPT_C_ENGINE,
     64 		.group	= FIO_OPT_G_E4DEFRAG,
     65 	},
     66 	{
     67 		.name	= NULL,
     68 	},
     69 };
     70 
     71 static int fio_e4defrag_init(struct thread_data *td)
     72 {
     73 	int r, len = 0;
     74 	struct e4defrag_options *o = td->eo;
     75 	struct e4defrag_data *ed;
     76 	struct stat stub;
     77 	char donor_name[PATH_MAX];
     78 
     79 	if (!strlen(o->donor_name)) {
     80 		log_err("'donorname' options required\n");
     81 		return 1;
     82 	}
     83 
     84 	ed = malloc(sizeof(*ed));
     85 	if (!ed) {
     86 		td_verror(td, ENOMEM, "io_queue_init");
     87 		return 1;
     88 	}
     89 	memset(ed, 0 ,sizeof(*ed));
     90 
     91 	if (td->o.directory)
     92 		len = sprintf(donor_name, "%s/", td->o.directory);
     93 	sprintf(donor_name + len, "%s", o->donor_name);
     94 
     95 	ed->donor_fd = open(donor_name, O_CREAT|O_WRONLY, 0644);
     96 	if (ed->donor_fd < 0) {
     97 		td_verror(td, errno, "io_queue_init");
     98 		log_err("Can't open donor file %s err:%d\n", donor_name, ed->donor_fd);
     99 		free(ed);
    100 		return 1;
    101 	}
    102 
    103 	if (!o->inplace) {
    104 		long long __len = td->o.file_size_high - td->o.start_offset;
    105 		r = fallocate(ed->donor_fd, 0, td->o.start_offset, __len);
    106 		if (r)
    107 			goto err;
    108 	}
    109 	r = fstat(ed->donor_fd, &stub);
    110 	if (r)
    111 		goto err;
    112 
    113 	ed->bsz = stub.st_blksize;
    114 	td->io_ops_data = ed;
    115 	return 0;
    116 err:
    117 	td_verror(td, errno, "io_queue_init");
    118 	close(ed->donor_fd);
    119 	free(ed);
    120 	return 1;
    121 }
    122 
    123 static void fio_e4defrag_cleanup(struct thread_data *td)
    124 {
    125 	struct e4defrag_data *ed = td->io_ops_data;
    126 	if (ed) {
    127 		if (ed->donor_fd >= 0)
    128 			close(ed->donor_fd);
    129 		free(ed);
    130 	}
    131 }
    132 
    133 
    134 static int fio_e4defrag_queue(struct thread_data *td, struct io_u *io_u)
    135 {
    136 
    137 	int ret;
    138 	unsigned long long len;
    139 	struct move_extent me;
    140 	struct fio_file *f = io_u->file;
    141 	struct e4defrag_data *ed = td->io_ops_data;
    142 	struct e4defrag_options *o = td->eo;
    143 
    144 	fio_ro_check(td, io_u);
    145 
    146 	/* Theoretically defragmentation should not change data, but it
    147 	 * changes data layout. So this function handle only DDIR_WRITE
    148 	 * in order to satisfy strict read only access pattern
    149 	 */
    150 	if (io_u->ddir != DDIR_WRITE) {
    151 		io_u->error = EINVAL;
    152 		return FIO_Q_COMPLETED;
    153 	}
    154 
    155 	if (o->inplace) {
    156 		ret = fallocate(ed->donor_fd, 0, io_u->offset, io_u->xfer_buflen);
    157 		if (ret)
    158 			goto out;
    159 	}
    160 
    161 	memset(&me, 0, sizeof(me));
    162 	me.donor_fd = ed->donor_fd;
    163 	me.orig_start = io_u->offset / ed->bsz;
    164 	me.donor_start = me.orig_start;
    165 	len = (io_u->offset + io_u->xfer_buflen + ed->bsz -1);
    166 	me.len = len / ed->bsz - me.orig_start;
    167 
    168 	ret = ioctl(f->fd, EXT4_IOC_MOVE_EXT, &me);
    169 	len = me.moved_len * ed->bsz;
    170 
    171 	if (len > io_u->xfer_buflen)
    172 		len = io_u->xfer_buflen;
    173 
    174 	if (len != io_u->xfer_buflen) {
    175 		if (len) {
    176 			io_u->resid = io_u->xfer_buflen - len;
    177 			io_u->error = 0;
    178 		} else {
    179 			/* access beyond i_size */
    180 			io_u->error = EINVAL;
    181 		}
    182 	}
    183 	if (ret)
    184 		io_u->error = errno;
    185 
    186 	if (o->inplace)
    187 		ret = ftruncate(ed->donor_fd, 0);
    188 out:
    189 	if (ret && !io_u->error)
    190 		io_u->error = errno;
    191 
    192 	return FIO_Q_COMPLETED;
    193 }
    194 
    195 static struct ioengine_ops ioengine = {
    196 	.name			= "e4defrag",
    197 	.version		= FIO_IOOPS_VERSION,
    198 	.init			= fio_e4defrag_init,
    199 	.queue			= fio_e4defrag_queue,
    200 	.open_file		= generic_open_file,
    201 	.close_file		= generic_close_file,
    202 	.get_file_size		= generic_get_file_size,
    203 	.flags			= FIO_SYNCIO,
    204 	.cleanup		= fio_e4defrag_cleanup,
    205 	.options		= options,
    206 	.option_struct_size	= sizeof(struct e4defrag_options),
    207 
    208 };
    209 
    210 static void fio_init fio_syncio_register(void)
    211 {
    212 	register_ioengine(&ioengine);
    213 }
    214 
    215 static void fio_exit fio_syncio_unregister(void)
    216 {
    217 	unregister_ioengine(&ioengine);
    218 }
    219