• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
fio_e4defrag_init(struct thread_data * td)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 
fio_e4defrag_cleanup(struct thread_data * td)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 
fio_e4defrag_queue(struct thread_data * td,struct io_u * io_u)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 
fio_syncio_register(void)210 static void fio_init fio_syncio_register(void)
211 {
212 	register_ioengine(&ioengine);
213 }
214 
fio_syncio_unregister(void)215 static void fio_exit fio_syncio_unregister(void)
216 {
217 	unregister_ioengine(&ioengine);
218 }
219