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