]>
Commit | Line | Data |
---|---|---|
72069be9 AB |
1 | /* |
2 | * Copyright (C) 2007 Nokia Corporation | |
3 | * | |
4 | * This program is free software; you can redistribute it and/or modify it | |
5 | * under the terms of the GNU General Public License version 2 as published by | |
6 | * the Free Software Foundation. | |
7 | * | |
8 | * This program is distributed in the hope that it will be useful, but WITHOUT | |
9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
10 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
11 | * more details. | |
12 | * | |
13 | * You should have received a copy of the GNU General Public License along with | |
14 | * this program; see the file COPYING. If not, write to the Free Software | |
15 | * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |
16 | * | |
17 | * Test read and write speed of a MTD device. | |
18 | * | |
fc7fe769 | 19 | * Author: Adrian Hunter <[email protected]> |
72069be9 AB |
20 | */ |
21 | ||
2c70d292 VN |
22 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
23 | ||
72069be9 | 24 | #include <linux/init.h> |
af30c0a0 | 25 | #include <linux/ktime.h> |
72069be9 AB |
26 | #include <linux/module.h> |
27 | #include <linux/moduleparam.h> | |
28 | #include <linux/err.h> | |
29 | #include <linux/mtd/mtd.h> | |
5a0e3ad6 | 30 | #include <linux/slab.h> |
72069be9 | 31 | #include <linux/sched.h> |
bfea1d4e | 32 | #include <linux/random.h> |
72069be9 | 33 | |
59b0816d AM |
34 | #include "mtd_test.h" |
35 | ||
7406060e | 36 | static int dev = -EINVAL; |
72069be9 AB |
37 | module_param(dev, int, S_IRUGO); |
38 | MODULE_PARM_DESC(dev, "MTD device number to use"); | |
39 | ||
fc7fe769 AH |
40 | static int count; |
41 | module_param(count, int, S_IRUGO); | |
42 | MODULE_PARM_DESC(count, "Maximum number of eraseblocks to use " | |
43 | "(0 means use all)"); | |
44 | ||
72069be9 AB |
45 | static struct mtd_info *mtd; |
46 | static unsigned char *iobuf; | |
47 | static unsigned char *bbt; | |
48 | ||
49 | static int pgsize; | |
50 | static int ebcnt; | |
51 | static int pgcnt; | |
52 | static int goodebcnt; | |
af30c0a0 | 53 | static ktime_t start, finish; |
72069be9 | 54 | |
4085bcc6 RT |
55 | static int multiblock_erase(int ebnum, int blocks) |
56 | { | |
57 | int err; | |
58 | struct erase_info ei; | |
1001ff7a | 59 | loff_t addr = (loff_t)ebnum * mtd->erasesize; |
4085bcc6 RT |
60 | |
61 | memset(&ei, 0, sizeof(struct erase_info)); | |
4085bcc6 RT |
62 | ei.addr = addr; |
63 | ei.len = mtd->erasesize * blocks; | |
64 | ||
7e1f0dc0 | 65 | err = mtd_erase(mtd, &ei); |
4085bcc6 | 66 | if (err) { |
2c70d292 | 67 | pr_err("error %d while erasing EB %d, blocks %d\n", |
4085bcc6 RT |
68 | err, ebnum, blocks); |
69 | return err; | |
70 | } | |
71 | ||
4085bcc6 RT |
72 | return 0; |
73 | } | |
74 | ||
72069be9 AB |
75 | static int write_eraseblock(int ebnum) |
76 | { | |
1001ff7a | 77 | loff_t addr = (loff_t)ebnum * mtd->erasesize; |
72069be9 | 78 | |
8a9f4aa3 | 79 | return mtdtest_write(mtd, addr, mtd->erasesize, iobuf); |
72069be9 AB |
80 | } |
81 | ||
82 | static int write_eraseblock_by_page(int ebnum) | |
83 | { | |
72069be9 | 84 | int i, err = 0; |
1001ff7a | 85 | loff_t addr = (loff_t)ebnum * mtd->erasesize; |
72069be9 AB |
86 | void *buf = iobuf; |
87 | ||
88 | for (i = 0; i < pgcnt; i++) { | |
59b0816d | 89 | err = mtdtest_write(mtd, addr, pgsize, buf); |
8a9f4aa3 | 90 | if (err) |
72069be9 | 91 | break; |
72069be9 AB |
92 | addr += pgsize; |
93 | buf += pgsize; | |
94 | } | |
95 | ||
96 | return err; | |
97 | } | |
98 | ||
99 | static int write_eraseblock_by_2pages(int ebnum) | |
100 | { | |
59b0816d | 101 | size_t sz = pgsize * 2; |
72069be9 | 102 | int i, n = pgcnt / 2, err = 0; |
1001ff7a | 103 | loff_t addr = (loff_t)ebnum * mtd->erasesize; |
72069be9 AB |
104 | void *buf = iobuf; |
105 | ||
106 | for (i = 0; i < n; i++) { | |
59b0816d | 107 | err = mtdtest_write(mtd, addr, sz, buf); |
8a9f4aa3 | 108 | if (err) |
72069be9 | 109 | return err; |
72069be9 AB |
110 | addr += sz; |
111 | buf += sz; | |
112 | } | |
8a9f4aa3 | 113 | if (pgcnt % 2) |
59b0816d | 114 | err = mtdtest_write(mtd, addr, pgsize, buf); |
72069be9 AB |
115 | |
116 | return err; | |
117 | } | |
118 | ||
119 | static int read_eraseblock(int ebnum) | |
120 | { | |
1001ff7a | 121 | loff_t addr = (loff_t)ebnum * mtd->erasesize; |
72069be9 | 122 | |
abc173ad | 123 | return mtdtest_read(mtd, addr, mtd->erasesize, iobuf); |
72069be9 AB |
124 | } |
125 | ||
126 | static int read_eraseblock_by_page(int ebnum) | |
127 | { | |
72069be9 | 128 | int i, err = 0; |
1001ff7a | 129 | loff_t addr = (loff_t)ebnum * mtd->erasesize; |
72069be9 AB |
130 | void *buf = iobuf; |
131 | ||
132 | for (i = 0; i < pgcnt; i++) { | |
59b0816d | 133 | err = mtdtest_read(mtd, addr, pgsize, buf); |
abc173ad | 134 | if (err) |
72069be9 | 135 | break; |
72069be9 AB |
136 | addr += pgsize; |
137 | buf += pgsize; | |
138 | } | |
139 | ||
140 | return err; | |
141 | } | |
142 | ||
143 | static int read_eraseblock_by_2pages(int ebnum) | |
144 | { | |
59b0816d | 145 | size_t sz = pgsize * 2; |
72069be9 | 146 | int i, n = pgcnt / 2, err = 0; |
1001ff7a | 147 | loff_t addr = (loff_t)ebnum * mtd->erasesize; |
72069be9 AB |
148 | void *buf = iobuf; |
149 | ||
150 | for (i = 0; i < n; i++) { | |
59b0816d | 151 | err = mtdtest_read(mtd, addr, sz, buf); |
abc173ad | 152 | if (err) |
72069be9 | 153 | return err; |
72069be9 AB |
154 | addr += sz; |
155 | buf += sz; | |
156 | } | |
abc173ad | 157 | if (pgcnt % 2) |
59b0816d | 158 | err = mtdtest_read(mtd, addr, pgsize, buf); |
72069be9 AB |
159 | |
160 | return err; | |
161 | } | |
162 | ||
72069be9 AB |
163 | static inline void start_timing(void) |
164 | { | |
af30c0a0 | 165 | start = ktime_get(); |
72069be9 AB |
166 | } |
167 | ||
168 | static inline void stop_timing(void) | |
169 | { | |
af30c0a0 | 170 | finish = ktime_get(); |
72069be9 AB |
171 | } |
172 | ||
173 | static long calc_speed(void) | |
174 | { | |
e70727e4 DL |
175 | uint64_t k; |
176 | long ms; | |
72069be9 | 177 | |
af30c0a0 | 178 | ms = ktime_ms_delta(finish, start); |
e70727e4 DL |
179 | if (ms == 0) |
180 | return 0; | |
b9da8bae | 181 | k = (uint64_t)goodebcnt * (mtd->erasesize / 1024) * 1000; |
e70727e4 DL |
182 | do_div(k, ms); |
183 | return k; | |
72069be9 AB |
184 | } |
185 | ||
72069be9 AB |
186 | static int __init mtd_speedtest_init(void) |
187 | { | |
4085bcc6 | 188 | int err, i, blocks, j, k; |
72069be9 AB |
189 | long speed; |
190 | uint64_t tmp; | |
191 | ||
192 | printk(KERN_INFO "\n"); | |
193 | printk(KERN_INFO "=================================================\n"); | |
7406060e WS |
194 | |
195 | if (dev < 0) { | |
064a7694 | 196 | pr_info("Please specify a valid mtd-device via module parameter\n"); |
2c70d292 | 197 | pr_crit("CAREFUL: This test wipes all data on the specified MTD device!\n"); |
7406060e WS |
198 | return -EINVAL; |
199 | } | |
200 | ||
fc7fe769 | 201 | if (count) |
2c70d292 | 202 | pr_info("MTD device: %d count: %d\n", dev, count); |
fc7fe769 | 203 | else |
2c70d292 | 204 | pr_info("MTD device: %d\n", dev); |
72069be9 AB |
205 | |
206 | mtd = get_mtd_device(NULL, dev); | |
207 | if (IS_ERR(mtd)) { | |
208 | err = PTR_ERR(mtd); | |
2c70d292 | 209 | pr_err("error: cannot get MTD device\n"); |
72069be9 AB |
210 | return err; |
211 | } | |
212 | ||
213 | if (mtd->writesize == 1) { | |
2c70d292 | 214 | pr_info("not NAND flash, assume page size is 512 " |
72069be9 AB |
215 | "bytes.\n"); |
216 | pgsize = 512; | |
217 | } else | |
218 | pgsize = mtd->writesize; | |
219 | ||
220 | tmp = mtd->size; | |
221 | do_div(tmp, mtd->erasesize); | |
222 | ebcnt = tmp; | |
f5e2bae0 | 223 | pgcnt = mtd->erasesize / pgsize; |
72069be9 | 224 | |
2c70d292 | 225 | pr_info("MTD device size %llu, eraseblock size %u, " |
72069be9 AB |
226 | "page size %u, count of eraseblocks %u, pages per " |
227 | "eraseblock %u, OOB size %u\n", | |
228 | (unsigned long long)mtd->size, mtd->erasesize, | |
229 | pgsize, ebcnt, pgcnt, mtd->oobsize); | |
230 | ||
fc7fe769 AH |
231 | if (count > 0 && count < ebcnt) |
232 | ebcnt = count; | |
233 | ||
72069be9 AB |
234 | err = -ENOMEM; |
235 | iobuf = kmalloc(mtd->erasesize, GFP_KERNEL); | |
33777e66 | 236 | if (!iobuf) |
72069be9 | 237 | goto out; |
72069be9 | 238 | |
99672f32 | 239 | prandom_bytes(iobuf, mtd->erasesize); |
72069be9 | 240 | |
59b0816d AM |
241 | bbt = kzalloc(ebcnt, GFP_KERNEL); |
242 | if (!bbt) | |
243 | goto out; | |
244 | err = mtdtest_scan_for_bad_eraseblocks(mtd, bbt, 0, ebcnt); | |
72069be9 AB |
245 | if (err) |
246 | goto out; | |
59b0816d AM |
247 | for (i = 0; i < ebcnt; i++) { |
248 | if (!bbt[i]) | |
249 | goodebcnt++; | |
250 | } | |
72069be9 | 251 | |
59b0816d | 252 | err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt); |
72069be9 AB |
253 | if (err) |
254 | goto out; | |
255 | ||
256 | /* Write all eraseblocks, 1 eraseblock at a time */ | |
2c70d292 | 257 | pr_info("testing eraseblock write speed\n"); |
72069be9 AB |
258 | start_timing(); |
259 | for (i = 0; i < ebcnt; ++i) { | |
260 | if (bbt[i]) | |
261 | continue; | |
262 | err = write_eraseblock(i); | |
263 | if (err) | |
264 | goto out; | |
2a6a28e7 RW |
265 | |
266 | err = mtdtest_relax(); | |
267 | if (err) | |
268 | goto out; | |
72069be9 AB |
269 | } |
270 | stop_timing(); | |
271 | speed = calc_speed(); | |
2c70d292 | 272 | pr_info("eraseblock write speed is %ld KiB/s\n", speed); |
72069be9 AB |
273 | |
274 | /* Read all eraseblocks, 1 eraseblock at a time */ | |
2c70d292 | 275 | pr_info("testing eraseblock read speed\n"); |
72069be9 AB |
276 | start_timing(); |
277 | for (i = 0; i < ebcnt; ++i) { | |
278 | if (bbt[i]) | |
279 | continue; | |
280 | err = read_eraseblock(i); | |
281 | if (err) | |
282 | goto out; | |
2a6a28e7 RW |
283 | |
284 | err = mtdtest_relax(); | |
285 | if (err) | |
286 | goto out; | |
72069be9 AB |
287 | } |
288 | stop_timing(); | |
289 | speed = calc_speed(); | |
2c70d292 | 290 | pr_info("eraseblock read speed is %ld KiB/s\n", speed); |
72069be9 | 291 | |
59b0816d | 292 | err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt); |
72069be9 AB |
293 | if (err) |
294 | goto out; | |
295 | ||
296 | /* Write all eraseblocks, 1 page at a time */ | |
2c70d292 | 297 | pr_info("testing page write speed\n"); |
72069be9 AB |
298 | start_timing(); |
299 | for (i = 0; i < ebcnt; ++i) { | |
300 | if (bbt[i]) | |
301 | continue; | |
302 | err = write_eraseblock_by_page(i); | |
303 | if (err) | |
304 | goto out; | |
2a6a28e7 RW |
305 | |
306 | err = mtdtest_relax(); | |
307 | if (err) | |
308 | goto out; | |
72069be9 AB |
309 | } |
310 | stop_timing(); | |
311 | speed = calc_speed(); | |
2c70d292 | 312 | pr_info("page write speed is %ld KiB/s\n", speed); |
72069be9 AB |
313 | |
314 | /* Read all eraseblocks, 1 page at a time */ | |
2c70d292 | 315 | pr_info("testing page read speed\n"); |
72069be9 AB |
316 | start_timing(); |
317 | for (i = 0; i < ebcnt; ++i) { | |
318 | if (bbt[i]) | |
319 | continue; | |
320 | err = read_eraseblock_by_page(i); | |
321 | if (err) | |
322 | goto out; | |
2a6a28e7 RW |
323 | |
324 | err = mtdtest_relax(); | |
325 | if (err) | |
326 | goto out; | |
72069be9 AB |
327 | } |
328 | stop_timing(); | |
329 | speed = calc_speed(); | |
2c70d292 | 330 | pr_info("page read speed is %ld KiB/s\n", speed); |
72069be9 | 331 | |
59b0816d | 332 | err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt); |
72069be9 AB |
333 | if (err) |
334 | goto out; | |
335 | ||
336 | /* Write all eraseblocks, 2 pages at a time */ | |
2c70d292 | 337 | pr_info("testing 2 page write speed\n"); |
72069be9 AB |
338 | start_timing(); |
339 | for (i = 0; i < ebcnt; ++i) { | |
340 | if (bbt[i]) | |
341 | continue; | |
342 | err = write_eraseblock_by_2pages(i); | |
343 | if (err) | |
344 | goto out; | |
2a6a28e7 RW |
345 | |
346 | err = mtdtest_relax(); | |
347 | if (err) | |
348 | goto out; | |
72069be9 AB |
349 | } |
350 | stop_timing(); | |
351 | speed = calc_speed(); | |
2c70d292 | 352 | pr_info("2 page write speed is %ld KiB/s\n", speed); |
72069be9 AB |
353 | |
354 | /* Read all eraseblocks, 2 pages at a time */ | |
2c70d292 | 355 | pr_info("testing 2 page read speed\n"); |
72069be9 AB |
356 | start_timing(); |
357 | for (i = 0; i < ebcnt; ++i) { | |
358 | if (bbt[i]) | |
359 | continue; | |
360 | err = read_eraseblock_by_2pages(i); | |
361 | if (err) | |
362 | goto out; | |
2a6a28e7 RW |
363 | |
364 | err = mtdtest_relax(); | |
365 | if (err) | |
366 | goto out; | |
72069be9 AB |
367 | } |
368 | stop_timing(); | |
369 | speed = calc_speed(); | |
2c70d292 | 370 | pr_info("2 page read speed is %ld KiB/s\n", speed); |
72069be9 AB |
371 | |
372 | /* Erase all eraseblocks */ | |
2c70d292 | 373 | pr_info("Testing erase speed\n"); |
72069be9 | 374 | start_timing(); |
59b0816d AM |
375 | err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt); |
376 | if (err) | |
377 | goto out; | |
72069be9 AB |
378 | stop_timing(); |
379 | speed = calc_speed(); | |
2c70d292 | 380 | pr_info("erase speed is %ld KiB/s\n", speed); |
72069be9 | 381 | |
4085bcc6 RT |
382 | /* Multi-block erase all eraseblocks */ |
383 | for (k = 1; k < 7; k++) { | |
384 | blocks = 1 << k; | |
2c70d292 | 385 | pr_info("Testing %dx multi-block erase speed\n", |
4085bcc6 RT |
386 | blocks); |
387 | start_timing(); | |
388 | for (i = 0; i < ebcnt; ) { | |
389 | for (j = 0; j < blocks && (i + j) < ebcnt; j++) | |
390 | if (bbt[i + j]) | |
391 | break; | |
392 | if (j < 1) { | |
393 | i++; | |
394 | continue; | |
395 | } | |
396 | err = multiblock_erase(i, j); | |
397 | if (err) | |
398 | goto out; | |
2a6a28e7 RW |
399 | |
400 | err = mtdtest_relax(); | |
401 | if (err) | |
402 | goto out; | |
403 | ||
4085bcc6 RT |
404 | i += j; |
405 | } | |
406 | stop_timing(); | |
407 | speed = calc_speed(); | |
2c70d292 | 408 | pr_info("%dx multi-block erase speed is %ld KiB/s\n", |
4085bcc6 RT |
409 | blocks, speed); |
410 | } | |
2c70d292 | 411 | pr_info("finished\n"); |
72069be9 AB |
412 | out: |
413 | kfree(iobuf); | |
414 | kfree(bbt); | |
415 | put_mtd_device(mtd); | |
416 | if (err) | |
2c70d292 | 417 | pr_info("error %d occurred\n", err); |
72069be9 AB |
418 | printk(KERN_INFO "=================================================\n"); |
419 | return err; | |
420 | } | |
421 | module_init(mtd_speedtest_init); | |
422 | ||
423 | static void __exit mtd_speedtest_exit(void) | |
424 | { | |
425 | return; | |
426 | } | |
427 | module_exit(mtd_speedtest_exit); | |
428 | ||
429 | MODULE_DESCRIPTION("Speed test module"); | |
430 | MODULE_AUTHOR("Adrian Hunter"); | |
431 | MODULE_LICENSE("GPL"); |