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