mtd: tests: add count parameter to mtd_speedtest
[deliverable/linux.git] / drivers / mtd / tests / mtd_speedtest.c
CommitLineData
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 <adrian.hunter@nokia.com>
72069be9
AB
20 */
21
22#include <linux/init.h>
23#include <linux/module.h>
24#include <linux/moduleparam.h>
25#include <linux/err.h>
26#include <linux/mtd/mtd.h>
5a0e3ad6 27#include <linux/slab.h>
72069be9
AB
28#include <linux/sched.h>
29
30#define PRINT_PREF KERN_INFO "mtd_speedtest: "
31
32static int dev;
33module_param(dev, int, S_IRUGO);
34MODULE_PARM_DESC(dev, "MTD device number to use");
35
fc7fe769
AH
36static int count;
37module_param(count, int, S_IRUGO);
38MODULE_PARM_DESC(count, "Maximum number of eraseblocks to use "
39 "(0 means use all)");
40
72069be9
AB
41static struct mtd_info *mtd;
42static unsigned char *iobuf;
43static unsigned char *bbt;
44
45static int pgsize;
46static int ebcnt;
47static int pgcnt;
48static int goodebcnt;
49static struct timeval start, finish;
50static unsigned long next = 1;
51
52static inline unsigned int simple_rand(void)
53{
54 next = next * 1103515245 + 12345;
55 return (unsigned int)((next / 65536) % 32768);
56}
57
58static inline void simple_srand(unsigned long seed)
59{
60 next = seed;
61}
62
63static void set_random_data(unsigned char *buf, size_t len)
64{
65 size_t i;
66
67 for (i = 0; i < len; ++i)
68 buf[i] = simple_rand();
69}
70
71static int erase_eraseblock(int ebnum)
72{
73 int err;
74 struct erase_info ei;
75 loff_t addr = ebnum * mtd->erasesize;
76
77 memset(&ei, 0, sizeof(struct erase_info));
78 ei.mtd = mtd;
79 ei.addr = addr;
80 ei.len = mtd->erasesize;
81
82 err = mtd->erase(mtd, &ei);
83 if (err) {
84 printk(PRINT_PREF "error %d while erasing EB %d\n", err, ebnum);
85 return err;
86 }
87
88 if (ei.state == MTD_ERASE_FAILED) {
89 printk(PRINT_PREF "some erase error occurred at EB %d\n",
90 ebnum);
91 return -EIO;
92 }
93
94 return 0;
95}
96
97static int erase_whole_device(void)
98{
99 int err;
100 unsigned int i;
101
102 for (i = 0; i < ebcnt; ++i) {
103 if (bbt[i])
104 continue;
105 err = erase_eraseblock(i);
106 if (err)
107 return err;
108 cond_resched();
109 }
110 return 0;
111}
112
113static int write_eraseblock(int ebnum)
114{
115 size_t written = 0;
116 int err = 0;
117 loff_t addr = ebnum * mtd->erasesize;
118
119 err = mtd->write(mtd, addr, mtd->erasesize, &written, iobuf);
120 if (err || written != mtd->erasesize) {
121 printk(PRINT_PREF "error: write failed at %#llx\n", addr);
122 if (!err)
123 err = -EINVAL;
124 }
125
126 return err;
127}
128
129static int write_eraseblock_by_page(int ebnum)
130{
131 size_t written = 0;
132 int i, err = 0;
133 loff_t addr = ebnum * mtd->erasesize;
134 void *buf = iobuf;
135
136 for (i = 0; i < pgcnt; i++) {
137 err = mtd->write(mtd, addr, pgsize, &written, buf);
138 if (err || written != pgsize) {
139 printk(PRINT_PREF "error: write failed at %#llx\n",
140 addr);
141 if (!err)
142 err = -EINVAL;
143 break;
144 }
145 addr += pgsize;
146 buf += pgsize;
147 }
148
149 return err;
150}
151
152static int write_eraseblock_by_2pages(int ebnum)
153{
154 size_t written = 0, sz = pgsize * 2;
155 int i, n = pgcnt / 2, err = 0;
156 loff_t addr = ebnum * mtd->erasesize;
157 void *buf = iobuf;
158
159 for (i = 0; i < n; i++) {
160 err = mtd->write(mtd, addr, sz, &written, buf);
161 if (err || written != sz) {
162 printk(PRINT_PREF "error: write failed at %#llx\n",
163 addr);
164 if (!err)
165 err = -EINVAL;
166 return err;
167 }
168 addr += sz;
169 buf += sz;
170 }
171 if (pgcnt % 2) {
172 err = mtd->write(mtd, addr, pgsize, &written, buf);
173 if (err || written != pgsize) {
174 printk(PRINT_PREF "error: write failed at %#llx\n",
175 addr);
176 if (!err)
177 err = -EINVAL;
178 }
179 }
180
181 return err;
182}
183
184static int read_eraseblock(int ebnum)
185{
186 size_t read = 0;
187 int err = 0;
188 loff_t addr = ebnum * mtd->erasesize;
189
190 err = mtd->read(mtd, addr, mtd->erasesize, &read, iobuf);
191 /* Ignore corrected ECC errors */
192 if (err == -EUCLEAN)
193 err = 0;
194 if (err || read != mtd->erasesize) {
195 printk(PRINT_PREF "error: read failed at %#llx\n", addr);
196 if (!err)
197 err = -EINVAL;
198 }
199
200 return err;
201}
202
203static int read_eraseblock_by_page(int ebnum)
204{
205 size_t read = 0;
206 int i, err = 0;
207 loff_t addr = ebnum * mtd->erasesize;
208 void *buf = iobuf;
209
210 for (i = 0; i < pgcnt; i++) {
211 err = mtd->read(mtd, addr, pgsize, &read, buf);
212 /* Ignore corrected ECC errors */
213 if (err == -EUCLEAN)
214 err = 0;
215 if (err || read != pgsize) {
216 printk(PRINT_PREF "error: read failed at %#llx\n",
217 addr);
218 if (!err)
219 err = -EINVAL;
220 break;
221 }
222 addr += pgsize;
223 buf += pgsize;
224 }
225
226 return err;
227}
228
229static int read_eraseblock_by_2pages(int ebnum)
230{
231 size_t read = 0, sz = pgsize * 2;
232 int i, n = pgcnt / 2, err = 0;
233 loff_t addr = ebnum * mtd->erasesize;
234 void *buf = iobuf;
235
236 for (i = 0; i < n; i++) {
237 err = mtd->read(mtd, addr, sz, &read, buf);
238 /* Ignore corrected ECC errors */
239 if (err == -EUCLEAN)
240 err = 0;
241 if (err || read != sz) {
242 printk(PRINT_PREF "error: read failed at %#llx\n",
243 addr);
244 if (!err)
245 err = -EINVAL;
246 return err;
247 }
248 addr += sz;
249 buf += sz;
250 }
251 if (pgcnt % 2) {
252 err = mtd->read(mtd, addr, pgsize, &read, buf);
253 /* Ignore corrected ECC errors */
254 if (err == -EUCLEAN)
255 err = 0;
256 if (err || read != pgsize) {
257 printk(PRINT_PREF "error: read failed at %#llx\n",
258 addr);
259 if (!err)
260 err = -EINVAL;
261 }
262 }
263
264 return err;
265}
266
267static int is_block_bad(int ebnum)
268{
269 loff_t addr = ebnum * mtd->erasesize;
270 int ret;
271
272 ret = mtd->block_isbad(mtd, addr);
273 if (ret)
274 printk(PRINT_PREF "block %d is bad\n", ebnum);
275 return ret;
276}
277
278static inline void start_timing(void)
279{
280 do_gettimeofday(&start);
281}
282
283static inline void stop_timing(void)
284{
285 do_gettimeofday(&finish);
286}
287
288static long calc_speed(void)
289{
290 long ms, k, speed;
291
292 ms = (finish.tv_sec - start.tv_sec) * 1000 +
293 (finish.tv_usec - start.tv_usec) / 1000;
294 k = goodebcnt * mtd->erasesize / 1024;
295 speed = (k * 1000) / ms;
296 return speed;
297}
298
299static int scan_for_bad_eraseblocks(void)
300{
301 int i, bad = 0;
302
2bfefa4c 303 bbt = kzalloc(ebcnt, GFP_KERNEL);
72069be9
AB
304 if (!bbt) {
305 printk(PRINT_PREF "error: cannot allocate memory\n");
306 return -ENOMEM;
307 }
72069be9 308
f5e2bae0
MTS
309 /* NOR flash does not implement block_isbad */
310 if (mtd->block_isbad == NULL)
311 goto out;
312
72069be9
AB
313 printk(PRINT_PREF "scanning for bad eraseblocks\n");
314 for (i = 0; i < ebcnt; ++i) {
315 bbt[i] = is_block_bad(i) ? 1 : 0;
316 if (bbt[i])
317 bad += 1;
318 cond_resched();
319 }
320 printk(PRINT_PREF "scanned %d eraseblocks, %d are bad\n", i, bad);
f5e2bae0 321out:
72069be9
AB
322 goodebcnt = ebcnt - bad;
323 return 0;
324}
325
326static int __init mtd_speedtest_init(void)
327{
328 int err, i;
329 long speed;
330 uint64_t tmp;
331
332 printk(KERN_INFO "\n");
333 printk(KERN_INFO "=================================================\n");
fc7fe769
AH
334 if (count)
335 printk(PRINT_PREF "MTD device: %d count: %d\n", dev, count);
336 else
337 printk(PRINT_PREF "MTD device: %d\n", dev);
72069be9
AB
338
339 mtd = get_mtd_device(NULL, dev);
340 if (IS_ERR(mtd)) {
341 err = PTR_ERR(mtd);
342 printk(PRINT_PREF "error: cannot get MTD device\n");
343 return err;
344 }
345
346 if (mtd->writesize == 1) {
347 printk(PRINT_PREF "not NAND flash, assume page size is 512 "
348 "bytes.\n");
349 pgsize = 512;
350 } else
351 pgsize = mtd->writesize;
352
353 tmp = mtd->size;
354 do_div(tmp, mtd->erasesize);
355 ebcnt = tmp;
f5e2bae0 356 pgcnt = mtd->erasesize / pgsize;
72069be9
AB
357
358 printk(PRINT_PREF "MTD device size %llu, eraseblock size %u, "
359 "page size %u, count of eraseblocks %u, pages per "
360 "eraseblock %u, OOB size %u\n",
361 (unsigned long long)mtd->size, mtd->erasesize,
362 pgsize, ebcnt, pgcnt, mtd->oobsize);
363
fc7fe769
AH
364 if (count > 0 && count < ebcnt)
365 ebcnt = count;
366
72069be9
AB
367 err = -ENOMEM;
368 iobuf = kmalloc(mtd->erasesize, GFP_KERNEL);
369 if (!iobuf) {
370 printk(PRINT_PREF "error: cannot allocate memory\n");
371 goto out;
372 }
373
374 simple_srand(1);
375 set_random_data(iobuf, mtd->erasesize);
376
377 err = scan_for_bad_eraseblocks();
378 if (err)
379 goto out;
380
381 err = erase_whole_device();
382 if (err)
383 goto out;
384
385 /* Write all eraseblocks, 1 eraseblock at a time */
386 printk(PRINT_PREF "testing eraseblock write speed\n");
387 start_timing();
388 for (i = 0; i < ebcnt; ++i) {
389 if (bbt[i])
390 continue;
391 err = write_eraseblock(i);
392 if (err)
393 goto out;
394 cond_resched();
395 }
396 stop_timing();
397 speed = calc_speed();
398 printk(PRINT_PREF "eraseblock write speed is %ld KiB/s\n", speed);
399
400 /* Read all eraseblocks, 1 eraseblock at a time */
401 printk(PRINT_PREF "testing eraseblock read speed\n");
402 start_timing();
403 for (i = 0; i < ebcnt; ++i) {
404 if (bbt[i])
405 continue;
406 err = read_eraseblock(i);
407 if (err)
408 goto out;
409 cond_resched();
410 }
411 stop_timing();
412 speed = calc_speed();
413 printk(PRINT_PREF "eraseblock read speed is %ld KiB/s\n", speed);
414
415 err = erase_whole_device();
416 if (err)
417 goto out;
418
419 /* Write all eraseblocks, 1 page at a time */
420 printk(PRINT_PREF "testing page write speed\n");
421 start_timing();
422 for (i = 0; i < ebcnt; ++i) {
423 if (bbt[i])
424 continue;
425 err = write_eraseblock_by_page(i);
426 if (err)
427 goto out;
428 cond_resched();
429 }
430 stop_timing();
431 speed = calc_speed();
432 printk(PRINT_PREF "page write speed is %ld KiB/s\n", speed);
433
434 /* Read all eraseblocks, 1 page at a time */
435 printk(PRINT_PREF "testing page read speed\n");
436 start_timing();
437 for (i = 0; i < ebcnt; ++i) {
438 if (bbt[i])
439 continue;
440 err = read_eraseblock_by_page(i);
441 if (err)
442 goto out;
443 cond_resched();
444 }
445 stop_timing();
446 speed = calc_speed();
447 printk(PRINT_PREF "page read speed is %ld KiB/s\n", speed);
448
449 err = erase_whole_device();
450 if (err)
451 goto out;
452
453 /* Write all eraseblocks, 2 pages at a time */
454 printk(PRINT_PREF "testing 2 page write speed\n");
455 start_timing();
456 for (i = 0; i < ebcnt; ++i) {
457 if (bbt[i])
458 continue;
459 err = write_eraseblock_by_2pages(i);
460 if (err)
461 goto out;
462 cond_resched();
463 }
464 stop_timing();
465 speed = calc_speed();
466 printk(PRINT_PREF "2 page write speed is %ld KiB/s\n", speed);
467
468 /* Read all eraseblocks, 2 pages at a time */
469 printk(PRINT_PREF "testing 2 page read speed\n");
470 start_timing();
471 for (i = 0; i < ebcnt; ++i) {
472 if (bbt[i])
473 continue;
474 err = read_eraseblock_by_2pages(i);
475 if (err)
476 goto out;
477 cond_resched();
478 }
479 stop_timing();
480 speed = calc_speed();
481 printk(PRINT_PREF "2 page read speed is %ld KiB/s\n", speed);
482
483 /* Erase all eraseblocks */
484 printk(PRINT_PREF "Testing erase speed\n");
485 start_timing();
486 for (i = 0; i < ebcnt; ++i) {
487 if (bbt[i])
488 continue;
489 err = erase_eraseblock(i);
490 if (err)
491 goto out;
492 cond_resched();
493 }
494 stop_timing();
495 speed = calc_speed();
496 printk(PRINT_PREF "erase speed is %ld KiB/s\n", speed);
497
498 printk(PRINT_PREF "finished\n");
499out:
500 kfree(iobuf);
501 kfree(bbt);
502 put_mtd_device(mtd);
503 if (err)
504 printk(PRINT_PREF "error %d occurred\n", err);
505 printk(KERN_INFO "=================================================\n");
506 return err;
507}
508module_init(mtd_speedtest_init);
509
510static void __exit mtd_speedtest_exit(void)
511{
512 return;
513}
514module_exit(mtd_speedtest_exit);
515
516MODULE_DESCRIPTION("Speed test module");
517MODULE_AUTHOR("Adrian Hunter");
518MODULE_LICENSE("GPL");
This page took 0.180486 seconds and 5 git commands to generate.