mmap: don't include MADV_FREE in fadvise_hint check
[fio.git] / engines / mtd.c
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 "../optgroup.h"
17 #include "../verify.h"
18 #include "../oslib/libmtd.h"
19
20 static libmtd_t desc;
21
22 struct fio_mtd_data {
23         struct mtd_dev_info info;
24 };
25
26 struct fio_mtd_options {
27         void *pad; /* avoid off1 == 0 */
28         unsigned int skip_bad;
29 };
30
31 static 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
48 static 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
64 static 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
77 static 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);
81         struct fio_mtd_options *o = td->eo;
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
104                 if (o->skip_bad) {
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
146 next:
147                 local_offs += len;
148         }
149
150         return FIO_Q_COMPLETED;
151 }
152
153 static 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
175 err_free:
176         free(fmd);
177 err_close:
178         {
179                 int fio_unused __ret;
180                 __ret = generic_close_file(td, f);
181                 return 1;
182         }
183 }
184
185 static 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
195 static int fio_mtd_get_file_size(struct thread_data *td, struct fio_file *f)
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
209 static 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,
217         .options        = options,
218         .option_struct_size     = sizeof(struct fio_mtd_options),
219 };
220
221 static void fio_init fio_mtd_register(void)
222 {
223         desc = libmtd_open();
224         register_ioengine(&ioengine);
225 }
226
227 static void fio_exit fio_mtd_unregister(void)
228 {
229         unregister_ioengine(&ioengine);
230         libmtd_close(desc);
231         desc = NULL;
232 }
233
234
235