Commit | Line | Data |
---|---|---|
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" | |
17 | #include "../lib/libmtd.h" | |
18 | ||
19 | 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 | 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 |