[MTD] [NAND] subpage read feature as a way to increase performance.

This patch enables NAND subpage read functionality.
If upper layer drivers are requesting to read non page aligned data NAND
subpage-read functionality reads the only whose ECC regions which include
requested data when original code reads whole page.
This significantly improves performance in many cases.

Here are some digits :

UBI volume mount time
No subpage reads: 5.75 seconds
Subpage read patch: 2.42 seconds

Open/stat time for files on JFFS2 volume:
No subpage read 0m 5.36s
Subpage read 0m 2.88s

Signed-off-by Alexey Korolev <akorolev@infradead.org>
Acked-by: Artem Bityutskiy <dedekind@infradead.org>
Acked-by: Jörn Engel <joern@logfs.org>
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>

authored by

Alexey Korolev and committed by
David Woodhouse
3d459559 ff877ea8

+91 -1
+86 -1
drivers/mtd/nand/nand_base.c
··· 798 } 799 800 /** 801 * nand_read_page_hwecc - [REPLACABLE] hardware ecc based page read function 802 * @mtd: mtd info structure 803 * @chip: nand chip info structure ··· 1075 /* Now read the page into the buffer */ 1076 if (unlikely(ops->mode == MTD_OOB_RAW)) 1077 ret = chip->ecc.read_page_raw(mtd, chip, bufpoi); 1078 else 1079 ret = chip->ecc.read_page(mtd, chip, bufpoi); 1080 if (ret < 0) ··· 1084 1085 /* Transfer not aligned data */ 1086 if (!aligned) { 1087 - chip->pagebuf = realpage; 1088 memcpy(buf, chip->buffers->databuf + col, bytes); 1089 } 1090 ··· 2605 chip->ecc.calculate = nand_calculate_ecc; 2606 chip->ecc.correct = nand_correct_data; 2607 chip->ecc.read_page = nand_read_page_swecc; 2608 chip->ecc.write_page = nand_write_page_swecc; 2609 chip->ecc.read_oob = nand_read_oob_std; 2610 chip->ecc.write_oob = nand_write_oob_std;
··· 798 } 799 800 /** 801 + * nand_read_subpage - [REPLACABLE] software ecc based sub-page read function 802 + * @mtd: mtd info structure 803 + * @chip: nand chip info structure 804 + * @dataofs offset of requested data within the page 805 + * @readlen data length 806 + * @buf: buffer to store read data 807 + */ 808 + static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, uint32_t data_offs, uint32_t readlen, uint8_t *bufpoi) 809 + { 810 + int start_step, end_step, num_steps; 811 + uint32_t *eccpos = chip->ecc.layout->eccpos; 812 + uint8_t *p; 813 + int data_col_addr, i, gaps = 0; 814 + int datafrag_len, eccfrag_len, aligned_len, aligned_pos; 815 + int busw = (chip->options & NAND_BUSWIDTH_16) ? 2 : 1; 816 + 817 + /* Column address wihin the page aligned to ECC size (256bytes). */ 818 + start_step = data_offs / chip->ecc.size; 819 + end_step = (data_offs + readlen - 1) / chip->ecc.size; 820 + num_steps = end_step - start_step + 1; 821 + 822 + /* Data size aligned to ECC ecc.size*/ 823 + datafrag_len = num_steps * chip->ecc.size; 824 + eccfrag_len = num_steps * chip->ecc.bytes; 825 + 826 + data_col_addr = start_step * chip->ecc.size; 827 + /* If we read not a page aligned data */ 828 + if (data_col_addr != 0) 829 + chip->cmdfunc(mtd, NAND_CMD_RNDOUT, data_col_addr, -1); 830 + 831 + p = bufpoi + data_col_addr; 832 + chip->read_buf(mtd, p, datafrag_len); 833 + 834 + /* Calculate ECC */ 835 + for (i = 0; i < eccfrag_len ; i += chip->ecc.bytes, p += chip->ecc.size) 836 + chip->ecc.calculate(mtd, p, &chip->buffers->ecccalc[i]); 837 + 838 + /* The performance is faster if to position offsets 839 + according to ecc.pos. Let make sure here that 840 + there are no gaps in ecc positions */ 841 + for (i = 0; i < eccfrag_len - 1; i++) { 842 + if (eccpos[i + start_step * chip->ecc.bytes] + 1 != 843 + eccpos[i + start_step * chip->ecc.bytes + 1]) { 844 + gaps = 1; 845 + break; 846 + } 847 + } 848 + if (gaps) { 849 + chip->cmdfunc(mtd, NAND_CMD_RNDOUT, mtd->writesize, -1); 850 + chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); 851 + } else { 852 + /* send the command to read the particular ecc bytes */ 853 + /* take care about buswidth alignment in read_buf */ 854 + aligned_pos = eccpos[start_step * chip->ecc.bytes] & ~(busw - 1); 855 + aligned_len = eccfrag_len; 856 + if (eccpos[start_step * chip->ecc.bytes] & (busw - 1)) 857 + aligned_len++; 858 + if (eccpos[(start_step + num_steps) * chip->ecc.bytes] & (busw - 1)) 859 + aligned_len++; 860 + 861 + chip->cmdfunc(mtd, NAND_CMD_RNDOUT, mtd->writesize + aligned_pos, -1); 862 + chip->read_buf(mtd, &chip->oob_poi[aligned_pos], aligned_len); 863 + } 864 + 865 + for (i = 0; i < eccfrag_len; i++) 866 + chip->buffers->ecccode[i] = chip->oob_poi[eccpos[i + start_step * chip->ecc.bytes]]; 867 + 868 + p = bufpoi + data_col_addr; 869 + for (i = 0; i < eccfrag_len ; i += chip->ecc.bytes, p += chip->ecc.size) { 870 + int stat; 871 + 872 + stat = chip->ecc.correct(mtd, p, &chip->buffers->ecccode[i], &chip->buffers->ecccalc[i]); 873 + if (stat == -1) 874 + mtd->ecc_stats.failed++; 875 + else 876 + mtd->ecc_stats.corrected += stat; 877 + } 878 + return 0; 879 + } 880 + 881 + /** 882 * nand_read_page_hwecc - [REPLACABLE] hardware ecc based page read function 883 * @mtd: mtd info structure 884 * @chip: nand chip info structure ··· 994 /* Now read the page into the buffer */ 995 if (unlikely(ops->mode == MTD_OOB_RAW)) 996 ret = chip->ecc.read_page_raw(mtd, chip, bufpoi); 997 + else if (!aligned && NAND_SUBPAGE_READ(chip) && !oob) 998 + ret = chip->ecc.read_subpage(mtd, chip, col, bytes, bufpoi); 999 else 1000 ret = chip->ecc.read_page(mtd, chip, bufpoi); 1001 if (ret < 0) ··· 1001 1002 /* Transfer not aligned data */ 1003 if (!aligned) { 1004 + if (!NAND_SUBPAGE_READ(chip) && !oob) 1005 + chip->pagebuf = realpage; 1006 memcpy(buf, chip->buffers->databuf + col, bytes); 1007 } 1008 ··· 2521 chip->ecc.calculate = nand_calculate_ecc; 2522 chip->ecc.correct = nand_correct_data; 2523 chip->ecc.read_page = nand_read_page_swecc; 2524 + chip->ecc.read_subpage = nand_read_subpage; 2525 chip->ecc.write_page = nand_write_page_swecc; 2526 chip->ecc.read_oob = nand_read_oob_std; 2527 chip->ecc.write_oob = nand_write_oob_std;
+5
include/linux/mtd/nand.h
··· 177 #define NAND_MUST_PAD(chip) (!(chip->options & NAND_NO_PADDING)) 178 #define NAND_HAS_CACHEPROG(chip) ((chip->options & NAND_CACHEPRG)) 179 #define NAND_HAS_COPYBACK(chip) ((chip->options & NAND_COPYBACK)) 180 181 /* Mask to zero out the chip options, which come from the id table */ 182 #define NAND_CHIPOPTIONS_MSK (0x0000ffff & ~NAND_NO_AUTOINCR) ··· 274 const uint8_t *buf); 275 int (*read_page)(struct mtd_info *mtd, 276 struct nand_chip *chip, 277 uint8_t *buf); 278 void (*write_page)(struct mtd_info *mtd, 279 struct nand_chip *chip,
··· 177 #define NAND_MUST_PAD(chip) (!(chip->options & NAND_NO_PADDING)) 178 #define NAND_HAS_CACHEPROG(chip) ((chip->options & NAND_CACHEPRG)) 179 #define NAND_HAS_COPYBACK(chip) ((chip->options & NAND_COPYBACK)) 180 + #define NAND_SUBPAGE_READ(chip) ((chip->ecc.mode == NAND_ECC_SOFT)) 181 182 /* Mask to zero out the chip options, which come from the id table */ 183 #define NAND_CHIPOPTIONS_MSK (0x0000ffff & ~NAND_NO_AUTOINCR) ··· 273 const uint8_t *buf); 274 int (*read_page)(struct mtd_info *mtd, 275 struct nand_chip *chip, 276 + uint8_t *buf); 277 + int (*read_subpage)(struct mtd_info *mtd, 278 + struct nand_chip *chip, 279 + uint32_t offs, uint32_t len, 280 uint8_t *buf); 281 void (*write_page)(struct mtd_info *mtd, 282 struct nand_chip *chip,