Merge branch 'histogram-delta' of https://github.com/cronburg/fio into histogram
[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 "../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