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