options: make the groups/categories constant
[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"
16#include "../verify.h"
984f30c9 17#include "../oslib/libmtd.h"
65fa28ca
DE
18
19libmtd_t desc;
20
21struct fio_mtd_data {
22 struct mtd_dev_info info;
23};
24
25static 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
41static 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
54static 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
122next:
123 local_offs += len;
124 }
125
126 return FIO_Q_COMPLETED;
127}
128
129static 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
151err_free:
152 free(fmd);
153err_close:
154 {
8a68c41c
JA
155 int fio_unused __ret;
156 __ret = generic_close_file(td, f);
65fa28ca
DE
157 return 1;
158 }
159}
160
161static 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
171int 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
185static 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
195static void fio_init fio_mtd_register(void)
196{
197 desc = libmtd_open();
198 register_ioengine(&ioengine);
199}
200
201static void fio_exit fio_mtd_unregister(void)
202{
203 unregister_ioengine(&ioengine);
204 libmtd_close(desc);
205 desc = NULL;
206}
207
208
209