[RFC][patch] NAND partial page read functionality
Alexey Korolev
akorolev at infradead.org
Thu Dec 13 13:15:12 EST 2007
Hi
Here is a patch providing partial page read functionality for NAND
devices.
In many cases it gives performacne boost. I've added this feature
enabling under chip->options flag.
Setting NAND_PART_READ option in board driver will enable this feature.
This is example how partial page read could affect stat time perfromance.
--------
Case 1: partial page read is OFF
[root at Linux /]#time ls -l /mnt/mtd8/media
-rw-r--r-- 1 root root 8388608 Jan 1 00:01 file1
-rw-r--r-- 1 root root 8388608 Jan 1 00:02 file2
-rw-r--r-- 1 root root 8388608 Jan 1 00:02 file3
-rw-r--r-- 1 root root 8388608 Jan 1 00:02 file4
-rw-r--r-- 1 root root 8388608 Jan 1 00:02 file5
-rw-r--r-- 1 root root 8388608 Jan 1 00:02 file6
real 0m 5.36s
user 0m 0.00s
sys 0m 5.36s
--------
Case 2: partial page read if ON
[root at Linux /]#time ls -l /mnt/mtd8/media
-rw-r--r-- 1 root root 8388608 Jan 1 00:01 file1
-rw-r--r-- 1 root root 8388608 Jan 1 00:02 file2
-rw-r--r-- 1 root root 8388608 Jan 1 00:02 file3
-rw-r--r-- 1 root root 8388608 Jan 1 00:02 file4
-rw-r--r-- 1 root root 8388608 Jan 1 00:02 file5
-rw-r--r-- 1 root root 8388608 Jan 1 00:02 file6
real 0m 3.00s
user 0m 0.01s
sys 0m 2.99s
There are many cases when it makes sense to use it.
Please find patch below. Your comments and suggestions are welcome.
Thanks,
Alexey
Signed-off-by Alexey Korolev <akorolev at infradead.org>
--------------
diff -aur clear_adv/drivers/mtd/nand/nand_base.c linux-2.6.23.8-adv/drivers/mtd/nand/nand_base.c
--- clear_adv/drivers/mtd/nand/nand_base.c 2007-11-29 17:46:12.000000000 +0300
+++ linux-2.6.23.8-adv/drivers/mtd/nand/nand_base.c 2007-12-13 18:24:08.000000000 +0300
@@ -830,6 +830,66 @@
}
/**
+ * nand_read_partial - [REPLACABLE] software ecc based partial page read function
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @dataofs offset of requested data within the page
+ * @readlen data length
+ * @buf: buffer to store read data
+ */
+static int nand_read_partial(struct mtd_info * mtd,struct nand_chip *chip, uint32_t data_offs, uint32_t readlen, uint8_t *bufpoi)
+{
+ int start_step, end_step, num_steps;
+ uint32_t *eccpos = chip->ecc.layout->eccpos;
+ uint8_t *p;
+ int data_col_addr, i;
+ int datafrag_len, eccfrag_len;
+
+ /* Column address wihin the page aligned to ECC size (256bytes). */
+ start_step = data_offs / chip->ecc.size;
+ end_step = (data_offs + readlen - 1) / chip->ecc.size;
+ num_steps = end_step - start_step + 1;
+
+ /* Data size aligned to ECC ecc.size*/
+ datafrag_len = num_steps * chip->ecc.size;
+ eccfrag_len = num_steps * chip->ecc.bytes;
+
+ data_col_addr = start_step * chip->ecc.size;
+ /* If we read not a page aligned data */
+ if (data_col_addr != 0) {
+ chip->cmdfunc(mtd, NAND_CMD_RNDOUT, data_col_addr, -1);
+ }
+
+ p = bufpoi + data_col_addr;
+ chip->read_buf(mtd, p, datafrag_len);
+
+ /* Calculate ECC */
+ for (i = 0; i<eccfrag_len ; i += chip->ecc.bytes, p += chip->ecc.size)
+ chip->ecc.calculate(mtd, p, &chip->buffers->ecccalc[i]);
+
+ /* The performance will be improved more if to position offsets
+ * according to ecc.pos. But in this case we may face issues
+ * on chips with gaps in ecc positions. So it is disabled yet*/
+ chip->cmdfunc(mtd, NAND_CMD_RNDOUT, mtd->writesize, -1);
+ chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
+ for (i = 0; i < eccfrag_len; i++)
+ chip->buffers->ecccode[i] = chip->oob_poi[eccpos[i + start_step * chip->ecc.bytes]];
+
+ p = bufpoi + data_col_addr;
+ for (i = 0; i<eccfrag_len ; i += chip->ecc.bytes, p += chip->ecc.size) {
+ int stat;
+
+ stat = chip->ecc.correct(mtd, p, &chip->buffers->ecccode[i], &chip->buffers->ecccalc[i]);
+ if (stat == -1)
+ mtd->ecc_stats.failed++;
+ else
+ mtd->ecc_stats.corrected += stat;
+ }
+ return 0;
+
+}
+
+/**
* nand_read_page_hwecc - [REPLACABLE] hardware ecc based page read function
* @mtd: mtd info structure
* @chip: nand chip info structure
@@ -1048,14 +1108,17 @@
/* Now read the page into the buffer */
if (unlikely(ops->mode == MTD_OOB_RAW))
ret = chip->ecc.read_page_raw(mtd, chip, bufpoi);
- else
+ else if ( !aligned && NAND_CANPARTREAD(chip) )
+ ret = chip->ecc.read_partial(mtd, chip, col, bytes, bufpoi);
+ else
ret = chip->ecc.read_page(mtd, chip, bufpoi);
if (ret < 0)
break;
/* Transfer not aligned data */
if (!aligned) {
- chip->pagebuf = realpage;
+ if (!NAND_CANPARTREAD(chip))
+ chip->pagebuf = realpage;
memcpy(buf, chip->buffers->databuf + col, bytes);
}
@@ -2563,6 +2626,7 @@
chip->ecc.calculate = nand_calculate_ecc;
chip->ecc.correct = nand_correct_data;
chip->ecc.read_page = nand_read_page_swecc;
+ chip->ecc.read_partial = nand_read_partial;
chip->ecc.write_page = nand_write_page_swecc;
chip->ecc.read_oob = nand_read_oob_std;
chip->ecc.write_oob = nand_write_oob_std;
diff -aur clear_adv/include/linux/mtd/nand.h linux-2.6.23.8-adv/include/linux/mtd/nand.h
--- clear_adv/include/linux/mtd/nand.h 2007-11-29 17:46:12.000000000 +0300
+++ linux-2.6.23.8-adv/include/linux/mtd/nand.h 2007-12-13 18:24:30.000000000 +0300
@@ -170,6 +170,8 @@
#define NAND_NO_READRDY 0x00000100
/* Chip does not allow subpage writes */
#define NAND_NO_SUBPAGE_WRITE 0x00000200
+/* Chip supports partial page read */
+#define NAND_PART_READ 0x00000400
/* Options valid for Samsung large page devices */
#define NAND_SAMSUNG_LP_OPTIONS \
@@ -183,6 +185,8 @@
#define NAND_MUST_PAD(chip) (!(chip->options & NAND_NO_PADDING))
#define NAND_HAS_CACHEPROG(chip) ((chip->options & NAND_CACHEPRG))
#define NAND_HAS_COPYBACK(chip) ((chip->options & NAND_COPYBACK))
+#define NAND_CANPARTREAD(chip) ((chip->options & NAND_PART_READ) &&\
+ (chip->ecc.mode == NAND_ECC_SOFT))
/* Mask to zero out the chip options, which come from the id table */
#define NAND_CHIPOPTIONS_MSK (0x0000ffff & ~NAND_NO_AUTOINCR)
@@ -281,6 +285,10 @@
int (*read_page)(struct mtd_info *mtd,
struct nand_chip *chip,
uint8_t *buf);
+ int (*read_partial)(struct mtd_info *mtd,
+ struct nand_chip *chip,
+ uint32_t offs, uint32_t len,
+ uint8_t *buf);
void (*write_page)(struct mtd_info *mtd,
struct nand_chip *chip,
const uint8_t *buf);
More information about the linux-mtd
mailing list