Split mutex.c and .h each into three files
[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 #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 }