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