1 /* 2 * MTD engine 3 * 4 * IO engine that reads/writes from MTD character devices. 5 * 6 */ 7 #include <assert.h> 8 #include <stdio.h> 9 #include <stdlib.h> 10 #include <unistd.h> 11 #include <errno.h> 12 #include <sys/ioctl.h> 13 #include <mtd/mtd-user.h> 14 15 #include "../fio.h" 16 #include "../verify.h" 17 #include "../oslib/libmtd.h" 18 19 static libmtd_t desc; 20 21 struct fio_mtd_data { 22 struct mtd_dev_info info; 23 }; 24 25 static int fio_mtd_maybe_mark_bad(struct thread_data *td, 26 struct fio_mtd_data *fmd, 27 struct io_u *io_u, int eb) 28 { 29 int ret; 30 if (errno == EIO) { 31 ret = mtd_mark_bad(&fmd->info, io_u->file->fd, eb); 32 if (ret != 0) { 33 io_u->error = errno; 34 td_verror(td, errno, "mtd_mark_bad"); 35 return -1; 36 } 37 } 38 return 0; 39 } 40 41 static int fio_mtd_is_bad(struct thread_data *td, 42 struct fio_mtd_data *fmd, 43 struct io_u *io_u, int eb) 44 { 45 int ret = mtd_is_bad(&fmd->info, io_u->file->fd, eb); 46 if (ret == -1) { 47 io_u->error = errno; 48 td_verror(td, errno, "mtd_is_bad"); 49 } else if (ret == 1) 50 io_u->error = EIO; /* Silent failure--don't flood stderr */ 51 return ret; 52 } 53 54 static int fio_mtd_queue(struct thread_data *td, struct io_u *io_u) 55 { 56 struct fio_file *f = io_u->file; 57 struct fio_mtd_data *fmd = FILE_ENG_DATA(f); 58 int local_offs = 0; 59 int ret; 60 61 fio_ro_check(td, io_u); 62 63 /* 64 * Errors tend to pertain to particular erase blocks, so divide up 65 * I/O to erase block size. 66 * If an error is encountered, log it and keep going onto the next 67 * block because the error probably just pertains to that block. 68 * TODO(dehrenberg): Divide up reads and writes into page-sized 69 * operations to get more fine-grained information about errors. 70 */ 71 while (local_offs < io_u->buflen) { 72 int eb = (io_u->offset + local_offs) / fmd->info.eb_size; 73 int eb_offs = (io_u->offset + local_offs) % fmd->info.eb_size; 74 /* The length is the smaller of the length remaining in the 75 * buffer and the distance to the end of the erase block */ 76 int len = min((int)io_u->buflen - local_offs, 77 (int)fmd->info.eb_size - eb_offs); 78 char *buf = ((char *)io_u->buf) + local_offs; 79 80 if (td->o.skip_bad) { 81 ret = fio_mtd_is_bad(td, fmd, io_u, eb); 82 if (ret == -1) 83 break; 84 else if (ret == 1) 85 goto next; 86 } 87 if (io_u->ddir == DDIR_READ) { 88 ret = mtd_read(&fmd->info, f->fd, eb, eb_offs, buf, len); 89 if (ret != 0) { 90 io_u->error = errno; 91 td_verror(td, errno, "mtd_read"); 92 if (fio_mtd_maybe_mark_bad(td, fmd, io_u, eb)) 93 break; 94 } 95 } else if (io_u->ddir == DDIR_WRITE) { 96 ret = mtd_write(desc, &fmd->info, f->fd, eb, 97 eb_offs, buf, len, NULL, 0, 0); 98 if (ret != 0) { 99 io_u->error = errno; 100 td_verror(td, errno, "mtd_write"); 101 if (fio_mtd_maybe_mark_bad(td, fmd, io_u, eb)) 102 break; 103 } 104 } else if (io_u->ddir == DDIR_TRIM) { 105 if (eb_offs != 0 || len != fmd->info.eb_size) { 106 io_u->error = EINVAL; 107 td_verror(td, EINVAL, 108 "trim on MTD must be erase block-aligned"); 109 } 110 ret = mtd_erase(desc, &fmd->info, f->fd, eb); 111 if (ret != 0) { 112 io_u->error = errno; 113 td_verror(td, errno, "mtd_erase"); 114 if (fio_mtd_maybe_mark_bad(td, fmd, io_u, eb)) 115 break; 116 } 117 } else { 118 io_u->error = ENOTSUP; 119 td_verror(td, io_u->error, "operation not supported on mtd"); 120 } 121 122 next: 123 local_offs += len; 124 } 125 126 return FIO_Q_COMPLETED; 127 } 128 129 static int fio_mtd_open_file(struct thread_data *td, struct fio_file *f) 130 { 131 struct fio_mtd_data *fmd; 132 int ret; 133 134 ret = generic_open_file(td, f); 135 if (ret) 136 return ret; 137 138 fmd = calloc(1, sizeof(*fmd)); 139 if (!fmd) 140 goto err_close; 141 142 ret = mtd_get_dev_info(desc, f->file_name, &fmd->info); 143 if (ret != 0) { 144 td_verror(td, errno, "mtd_get_dev_info"); 145 goto err_free; 146 } 147 148 FILE_SET_ENG_DATA(f, fmd); 149 return 0; 150 151 err_free: 152 free(fmd); 153 err_close: 154 { 155 int fio_unused __ret; 156 __ret = generic_close_file(td, f); 157 return 1; 158 } 159 } 160 161 static int fio_mtd_close_file(struct thread_data *td, struct fio_file *f) 162 { 163 struct fio_mtd_data *fmd = FILE_ENG_DATA(f); 164 165 FILE_SET_ENG_DATA(f, NULL); 166 free(fmd); 167 168 return generic_close_file(td, f); 169 } 170 171 static int fio_mtd_get_file_size(struct thread_data *td, struct fio_file *f) 172 { 173 struct mtd_dev_info info; 174 175 int ret = mtd_get_dev_info(desc, f->file_name, &info); 176 if (ret != 0) { 177 td_verror(td, errno, "mtd_get_dev_info"); 178 return errno; 179 } 180 f->real_file_size = info.size; 181 182 return 0; 183 } 184 185 static struct ioengine_ops ioengine = { 186 .name = "mtd", 187 .version = FIO_IOOPS_VERSION, 188 .queue = fio_mtd_queue, 189 .open_file = fio_mtd_open_file, 190 .close_file = fio_mtd_close_file, 191 .get_file_size = fio_mtd_get_file_size, 192 .flags = FIO_SYNCIO | FIO_NOEXTEND, 193 }; 194 195 static void fio_init fio_mtd_register(void) 196 { 197 desc = libmtd_open(); 198 register_ioengine(&ioengine); 199 } 200 201 static void fio_exit fio_mtd_unregister(void) 202 { 203 unregister_ioengine(&ioengine); 204 libmtd_close(desc); 205 desc = NULL; 206 } 207 208 209 210