4 * IO engine that reads/writes from MTD character devices.
10 #include <sys/ioctl.h>
11 #include <mtd/mtd-user.h>
14 #include "../optgroup.h"
15 #include "../oslib/libmtd.h"
20 struct mtd_dev_info info;
23 struct fio_mtd_options {
24 void *pad; /* avoid off1 == 0 */
25 unsigned int skip_bad;
28 static struct fio_option options[] = {
31 .lname = "Skip operations against bad blocks",
33 .off1 = offsetof(struct fio_mtd_options, skip_bad),
34 .help = "Skip operations against known bad blocks.",
37 .category = FIO_OPT_C_ENGINE,
38 .group = FIO_OPT_G_MTD,
45 static int fio_mtd_maybe_mark_bad(struct thread_data *td,
46 struct fio_mtd_data *fmd,
47 struct io_u *io_u, int eb)
51 ret = mtd_mark_bad(&fmd->info, io_u->file->fd, eb);
54 td_verror(td, errno, "mtd_mark_bad");
61 static int fio_mtd_is_bad(struct thread_data *td,
62 struct fio_mtd_data *fmd,
63 struct io_u *io_u, int eb)
65 int ret = mtd_is_bad(&fmd->info, io_u->file->fd, eb);
68 td_verror(td, errno, "mtd_is_bad");
70 io_u->error = EIO; /* Silent failure--don't flood stderr */
74 static int fio_mtd_queue(struct thread_data *td, struct io_u *io_u)
76 struct fio_file *f = io_u->file;
77 struct fio_mtd_data *fmd = FILE_ENG_DATA(f);
78 struct fio_mtd_options *o = td->eo;
82 fio_ro_check(td, io_u);
85 * Errors tend to pertain to particular erase blocks, so divide up
86 * I/O to erase block size.
87 * If an error is encountered, log it and keep going onto the next
88 * block because the error probably just pertains to that block.
89 * TODO(dehrenberg): Divide up reads and writes into page-sized
90 * operations to get more fine-grained information about errors.
92 while (local_offs < io_u->buflen) {
93 int eb = (io_u->offset + local_offs) / fmd->info.eb_size;
94 int eb_offs = (io_u->offset + local_offs) % fmd->info.eb_size;
95 /* The length is the smaller of the length remaining in the
96 * buffer and the distance to the end of the erase block */
97 int len = min((int)io_u->buflen - local_offs,
98 (int)fmd->info.eb_size - eb_offs);
99 char *buf = ((char *)io_u->buf) + local_offs;
102 ret = fio_mtd_is_bad(td, fmd, io_u, eb);
108 if (io_u->ddir == DDIR_READ) {
109 ret = mtd_read(&fmd->info, f->fd, eb, eb_offs, buf, len);
112 td_verror(td, errno, "mtd_read");
113 if (fio_mtd_maybe_mark_bad(td, fmd, io_u, eb))
116 } else if (io_u->ddir == DDIR_WRITE) {
117 ret = mtd_write(desc, &fmd->info, f->fd, eb,
118 eb_offs, buf, len, NULL, 0, 0);
121 td_verror(td, errno, "mtd_write");
122 if (fio_mtd_maybe_mark_bad(td, fmd, io_u, eb))
125 } else if (io_u->ddir == DDIR_TRIM) {
126 if (eb_offs != 0 || len != fmd->info.eb_size) {
127 io_u->error = EINVAL;
128 td_verror(td, EINVAL,
129 "trim on MTD must be erase block-aligned");
131 ret = mtd_erase(desc, &fmd->info, f->fd, eb);
134 td_verror(td, errno, "mtd_erase");
135 if (fio_mtd_maybe_mark_bad(td, fmd, io_u, eb))
139 io_u->error = ENOTSUP;
140 td_verror(td, io_u->error, "operation not supported on mtd");
147 return FIO_Q_COMPLETED;
150 static int fio_mtd_open_file(struct thread_data *td, struct fio_file *f)
152 struct fio_mtd_data *fmd;
155 ret = generic_open_file(td, f);
159 fmd = calloc(1, sizeof(*fmd));
163 ret = mtd_get_dev_info(desc, f->file_name, &fmd->info);
165 td_verror(td, errno, "mtd_get_dev_info");
169 FILE_SET_ENG_DATA(f, fmd);
176 int fio_unused __ret;
177 __ret = generic_close_file(td, f);
182 static int fio_mtd_close_file(struct thread_data *td, struct fio_file *f)
184 struct fio_mtd_data *fmd = FILE_ENG_DATA(f);
186 FILE_SET_ENG_DATA(f, NULL);
189 return generic_close_file(td, f);
192 static int fio_mtd_get_file_size(struct thread_data *td, struct fio_file *f)
194 struct mtd_dev_info info;
196 int ret = mtd_get_dev_info(desc, f->file_name, &info);
198 td_verror(td, errno, "mtd_get_dev_info");
201 f->real_file_size = info.size;
206 static struct ioengine_ops ioengine = {
208 .version = FIO_IOOPS_VERSION,
209 .queue = fio_mtd_queue,
210 .open_file = fio_mtd_open_file,
211 .close_file = fio_mtd_close_file,
212 .get_file_size = fio_mtd_get_file_size,
213 .flags = FIO_SYNCIO | FIO_NOEXTEND,
215 .option_struct_size = sizeof(struct fio_mtd_options),
218 static void fio_init fio_mtd_register(void)
220 desc = libmtd_open();
221 register_ioengine(&ioengine);
224 static void fio_exit fio_mtd_unregister(void)
226 unregister_ioengine(&ioengine);