Merge branch 'include_refactor' of https://github.com/sitsofe/fio
[fio.git] / engines / e4defrag.c
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
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 (!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
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
130 static int fio_e4defrag_queue(struct thread_data *td, struct io_u *io_u)
131 {
132
133         int ret;
134         unsigned long long len;
135         struct move_extent me;
136         struct fio_file *f = io_u->file;
137         struct e4defrag_data *ed = td->io_ops_data;
138         struct e4defrag_options *o = td->eo;
139
140         fio_ro_check(td, io_u);
141
142         /* Theoretically defragmentation should not change data, but it
143          * changes data layout. So this function handle only DDIR_WRITE
144          * in order to satisfy strict read only access pattern
145          */
146         if (io_u->ddir != DDIR_WRITE) {
147                 io_u->error = EINVAL;
148                 return FIO_Q_COMPLETED;
149         }
150
151         if (o->inplace) {
152                 ret = fallocate(ed->donor_fd, 0, io_u->offset, io_u->xfer_buflen);
153                 if (ret)
154                         goto out;
155         }
156
157         memset(&me, 0, sizeof(me));
158         me.donor_fd = ed->donor_fd;
159         me.orig_start = io_u->offset / ed->bsz;
160         me.donor_start = me.orig_start;
161         len = (io_u->offset + io_u->xfer_buflen + ed->bsz -1);
162         me.len = len / ed->bsz - me.orig_start;
163
164         ret = ioctl(f->fd, EXT4_IOC_MOVE_EXT, &me);
165         len = me.moved_len * ed->bsz;
166
167         if (len > io_u->xfer_buflen)
168                 len = io_u->xfer_buflen;
169
170         if (len != io_u->xfer_buflen) {
171                 if (len) {
172                         io_u->resid = io_u->xfer_buflen - len;
173                         io_u->error = 0;
174                 } else {
175                         /* access beyond i_size */
176                         io_u->error = EINVAL;
177                 }
178         }
179         if (ret)
180                 io_u->error = errno;
181
182         if (o->inplace)
183                 ret = ftruncate(ed->donor_fd, 0);
184 out:
185         if (ret && !io_u->error)
186                 io_u->error = errno;
187
188         return FIO_Q_COMPLETED;
189 }
190
191 static struct ioengine_ops ioengine = {
192         .name                   = "e4defrag",
193         .version                = FIO_IOOPS_VERSION,
194         .init                   = fio_e4defrag_init,
195         .queue                  = fio_e4defrag_queue,
196         .open_file              = generic_open_file,
197         .close_file             = generic_close_file,
198         .get_file_size          = generic_get_file_size,
199         .flags                  = FIO_SYNCIO,
200         .cleanup                = fio_e4defrag_cleanup,
201         .options                = options,
202         .option_struct_size     = sizeof(struct e4defrag_options),
203
204 };
205
206 static void fio_init fio_syncio_register(void)
207 {
208         register_ioengine(&ioengine);
209 }
210
211 static void fio_exit fio_syncio_unregister(void)
212 {
213         unregister_ioengine(&ioengine);
214 }