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