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