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) 24struct 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 34struct e4defrag_data { 35 int donor_fd; 36 int bsz; 37}; 38 39struct e4defrag_options { 40 void *pad; 41 unsigned int inplace; 42 char * donor_name; 43}; 44 45static 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 71static 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; 116err: 117 td_verror(td, errno, "io_queue_init"); 118 close(ed->donor_fd); 119 free(ed); 120 return 1; 121} 122 123static 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 134static 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); 188out: 189 if (ret && !io_u->error) 190 io_u->error = errno; 191 192 return FIO_Q_COMPLETED; 193} 194 195static 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 210static void fio_init fio_syncio_register(void) 211{ 212 register_ioengine(&ioengine); 213} 214 215static void fio_exit fio_syncio_unregister(void) 216{ 217 unregister_ioengine(&ioengine); 218} 219