aboutsummaryrefslogtreecommitdiffstats
path: root/fs/jffs2/wbuf.c
diff options
context:
space:
mode:
authorThomas Gleixner <tglx@cruncher.tec.linutronix.de>2006-05-28 21:26:58 -0400
committerThomas Gleixner <tglx@cruncher.tec.linutronix.de>2006-05-29 09:06:51 -0400
commit8593fbc68b0df1168995de76d1af38eb62fd6b62 (patch)
treedd244def53d2be4f1fbff9f74eac404fab8e240f /fs/jffs2/wbuf.c
parentf4a43cfcecfcaeeaa40a9dbc1d1378298c22446e (diff)
[MTD] Rework the out of band handling completely
Hopefully the last iteration on this! The handling of out of band data on NAND was accompanied by tons of fruitless discussions and halfarsed patches to make it work for a particular problem. Sufficiently annoyed by I all those "I know it better" mails and the resonable amount of discarded "it solves my problem" patches, I finally decided to go for the big rework. After removing the _ecc variants of mtd read/write functions the solution to satisfy the various requirements was to refactor the read/write _oob functions in mtd. The major change is that read/write_oob now takes a pointer to an operation descriptor structure "struct mtd_oob_ops".instead of having a function with at least seven arguments. read/write_oob which should probably renamed to a more descriptive name, can do the following tasks: - read/write out of band data - read/write data content and out of band data - read/write raw data content and out of band data (ecc disabled) struct mtd_oob_ops has a mode field, which determines the oob handling mode. Aside of the MTD_OOB_RAW mode, which is intended to be especially for diagnostic purposes and some internal functions e.g. bad block table creation, the other two modes are for mtd clients: MTD_OOB_PLACE puts/gets the given oob data exactly to/from the place which is described by the ooboffs and ooblen fields of the mtd_oob_ops strcuture. It's up to the caller to make sure that the byte positions are not used by the ECC placement algorithms. MTD_OOB_AUTO puts/gets the given oob data automaticaly to/from the places in the out of band area which are described by the oobfree tuples in the ecclayout data structre which is associated to the devicee. The decision whether data plus oob or oob only handling is done depends on the setting of the datbuf member of the data structure. When datbuf == NULL then the internal read/write_oob functions are selected, otherwise the read/write data routines are invoked. Tested on a few platforms with all variants. Please be aware of possible regressions for your particular device / application scenario Disclaimer: Any whining will be ignored from those who just contributed "hot air blurb" and never sat down to tackle the underlying problem of the mess in the NAND driver grown over time and the big chunk of work to fix up the existing users. The problem was not the holiness of the existing MTD interfaces. The problems was the lack of time to go for the big overhaul. It's easy to add more mess to the existing one, but it takes alot of effort to go for a real solution. Improvements and bugfixes are welcome! Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Diffstat (limited to 'fs/jffs2/wbuf.c')
-rw-r--r--fs/jffs2/wbuf.c230
1 files changed, 118 insertions, 112 deletions
diff --git a/fs/jffs2/wbuf.c b/fs/jffs2/wbuf.c
index c6a62e162963..1195d06d4373 100644
--- a/fs/jffs2/wbuf.c
+++ b/fs/jffs2/wbuf.c
@@ -955,158 +955,159 @@ exit:
955 return ret; 955 return ret;
956} 956}
957 957
958#define NR_OOB_SCAN_PAGES 4
959
958/* 960/*
959 * Check, if the out of band area is empty 961 * Check, if the out of band area is empty
960 */ 962 */
961int jffs2_check_oob_empty( struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, int mode) 963int jffs2_check_oob_empty(struct jffs2_sb_info *c,
964 struct jffs2_eraseblock *jeb, int mode)
962{ 965{
963 unsigned char *buf; 966 int i, page, ret;
964 int ret = 0; 967 int oobsize = c->mtd->oobsize;
965 int i,len,page; 968 struct mtd_oob_ops ops;
966 size_t retlen; 969
967 int oob_size; 970 ops.len = NR_OOB_SCAN_PAGES * oobsize;
968 971 ops.ooblen = oobsize;
969 /* allocate a buffer for all oob data in this sector */ 972 ops.oobbuf = c->oobbuf;
970 oob_size = c->mtd->oobsize; 973 ops.ooboffs = 0;
971 len = 4 * oob_size; 974 ops.datbuf = NULL;
972 buf = kmalloc(len, GFP_KERNEL); 975 ops.mode = MTD_OOB_PLACE;
973 if (!buf) { 976
974 printk(KERN_NOTICE "jffs2_check_oob_empty(): allocation of temporary data buffer for oob check failed\n"); 977 ret = c->mtd->read_oob(c->mtd, jeb->offset, &ops);
975 return -ENOMEM;
976 }
977 /*
978 * if mode = 0, we scan for a total empty oob area, else we have
979 * to take care of the cleanmarker in the first page of the block
980 */
981 ret = jffs2_flash_read_oob(c, jeb->offset, len , &retlen, buf);
982 if (ret) { 978 if (ret) {
983 D1(printk(KERN_WARNING "jffs2_check_oob_empty(): Read OOB failed %d for block at %08x\n", ret, jeb->offset)); 979 D1(printk(KERN_WARNING "jffs2_check_oob_empty(): Read OOB "
984 goto out; 980 "failed %d for block at %08x\n", ret, jeb->offset));
981 return ret;
985 } 982 }
986 983
987 if (retlen < len) { 984 if (ops.retlen < ops.len) {
988 D1(printk(KERN_WARNING "jffs2_check_oob_empty(): Read OOB return short read " 985 D1(printk(KERN_WARNING "jffs2_check_oob_empty(): Read OOB "
989 "(%zd bytes not %d) for block at %08x\n", retlen, len, jeb->offset)); 986 "returned short read (%zd bytes not %d) for block "
990 ret = -EIO; 987 "at %08x\n", ops.retlen, ops.len, jeb->offset));
991 goto out; 988 return -EIO;
992 } 989 }
993 990
994 /* Special check for first page */ 991 /* Special check for first page */
995 for(i = 0; i < oob_size ; i++) { 992 for(i = 0; i < oobsize ; i++) {
996 /* Yeah, we know about the cleanmarker. */ 993 /* Yeah, we know about the cleanmarker. */
997 if (mode && i >= c->fsdata_pos && 994 if (mode && i >= c->fsdata_pos &&
998 i < c->fsdata_pos + c->fsdata_len) 995 i < c->fsdata_pos + c->fsdata_len)
999 continue; 996 continue;
1000 997
1001 if (buf[i] != 0xFF) { 998 if (ops.oobbuf[i] != 0xFF) {
1002 D2(printk(KERN_DEBUG "Found %02x at %x in OOB for %08x\n", 999 D2(printk(KERN_DEBUG "Found %02x at %x in OOB for "
1003 buf[i], i, jeb->offset)); 1000 "%08x\n", ops.oobbuf[i], i, jeb->offset));
1004 ret = 1; 1001 return 1;
1005 goto out;
1006 } 1002 }
1007 } 1003 }
1008 1004
1009 /* we know, we are aligned :) */ 1005 /* we know, we are aligned :) */
1010 for (page = oob_size; page < len; page += sizeof(long)) { 1006 for (page = oobsize; page < ops.len; page += sizeof(long)) {
1011 unsigned long dat = *(unsigned long *)(&buf[page]); 1007 long dat = *(long *)(&ops.oobbuf[page]);
1012 if(dat != -1) { 1008 if(dat != -1)
1013 ret = 1; 1009 return 1;
1014 goto out;
1015 }
1016 } 1010 }
1017 1011 return 0;
1018out:
1019 kfree(buf);
1020
1021 return ret;
1022} 1012}
1023 1013
1024/* 1014/*
1025* Scan for a valid cleanmarker and for bad blocks 1015 * Scan for a valid cleanmarker and for bad blocks
1026* For virtual blocks (concatenated physical blocks) check the cleanmarker 1016 */
1027* only in the first page of the first physical block, but scan for bad blocks in all 1017int jffs2_check_nand_cleanmarker (struct jffs2_sb_info *c,
1028* physical blocks 1018 struct jffs2_eraseblock *jeb)
1029*/
1030int jffs2_check_nand_cleanmarker (struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
1031{ 1019{
1032 struct jffs2_unknown_node n; 1020 struct jffs2_unknown_node n;
1033 unsigned char buf[2 * NAND_MAX_OOBSIZE]; 1021 struct mtd_oob_ops ops;
1034 unsigned char *p; 1022 int oobsize = c->mtd->oobsize;
1035 int ret, i, cnt, retval = 0; 1023 unsigned char *p,*b;
1036 size_t retlen, offset; 1024 int i, ret;
1037 int oob_size; 1025 size_t offset = jeb->offset;
1038 1026
1039 offset = jeb->offset; 1027 /* Check first if the block is bad. */
1040 oob_size = c->mtd->oobsize; 1028 if (c->mtd->block_isbad(c->mtd, offset)) {
1041 1029 D1 (printk(KERN_WARNING "jffs2_check_nand_cleanmarker()"
1042 /* Loop through the physical blocks */ 1030 ": Bad block at %08x\n", jeb->offset));
1043 for (cnt = 0; cnt < (c->sector_size / c->mtd->erasesize); cnt++) { 1031 return 2;
1044 /* Check first if the block is bad. */ 1032 }
1045 if (c->mtd->block_isbad (c->mtd, offset)) {
1046 D1 (printk (KERN_WARNING "jffs2_check_nand_cleanmarker(): Bad block at %08x\n", jeb->offset));
1047 return 2;
1048 }
1049 /*
1050 * We read oob data from page 0 and 1 of the block.
1051 * page 0 contains cleanmarker and badblock info
1052 * page 1 contains failure count of this block
1053 */
1054 ret = c->mtd->read_oob (c->mtd, offset, oob_size << 1, &retlen, buf);
1055 1033
1056 if (ret) { 1034 ops.len = oobsize;
1057 D1 (printk (KERN_WARNING "jffs2_check_nand_cleanmarker(): Read OOB failed %d for block at %08x\n", ret, jeb->offset)); 1035 ops.ooblen = oobsize;
1058 return ret; 1036 ops.oobbuf = c->oobbuf;
1059 } 1037 ops.ooboffs = 0;
1060 if (retlen < (oob_size << 1)) { 1038 ops.datbuf = NULL;
1061 D1 (printk (KERN_WARNING "jffs2_check_nand_cleanmarker(): Read OOB return short read (%zd bytes not %d) for block at %08x\n", retlen, oob_size << 1, jeb->offset)); 1039 ops.mode = MTD_OOB_PLACE;
1062 return -EIO;
1063 }
1064 1040
1065 /* Check cleanmarker only on the first physical block */ 1041 ret = c->mtd->read_oob(c->mtd, offset, &ops);
1066 if (!cnt) { 1042 if (ret) {
1067 n.magic = cpu_to_je16 (JFFS2_MAGIC_BITMASK); 1043 D1 (printk(KERN_WARNING "jffs2_check_nand_cleanmarker(): "
1068 n.nodetype = cpu_to_je16 (JFFS2_NODETYPE_CLEANMARKER); 1044 "Read OOB failed %d for block at %08x\n",
1069 n.totlen = cpu_to_je32 (8); 1045 ret, jeb->offset));
1070 p = (unsigned char *) &n; 1046 return ret;
1047 }
1071 1048
1072 for (i = 0; i < c->fsdata_len; i++) { 1049 if (ops.retlen < ops.len) {
1073 if (buf[c->fsdata_pos + i] != p[i]) { 1050 D1 (printk (KERN_WARNING "jffs2_check_nand_cleanmarker(): "
1074 retval = 1; 1051 "Read OOB return short read (%zd bytes not %d) "
1075 } 1052 "for block at %08x\n", ops.retlen, ops.len,
1076 } 1053 jeb->offset));
1077 D1(if (retval == 1) { 1054 return -EIO;
1078 printk(KERN_WARNING "jffs2_check_nand_cleanmarker(): Cleanmarker node not detected in block at %08x\n", jeb->offset);
1079 printk(KERN_WARNING "OOB at %08zx was ", offset);
1080 for (i=0; i < oob_size; i++) {
1081 printk("%02x ", buf[i]);
1082 }
1083 printk("\n");
1084 })
1085 }
1086 offset += c->mtd->erasesize;
1087 } 1055 }
1088 return retval; 1056
1057 n.magic = cpu_to_je16 (JFFS2_MAGIC_BITMASK);
1058 n.nodetype = cpu_to_je16 (JFFS2_NODETYPE_CLEANMARKER);
1059 n.totlen = cpu_to_je32 (8);
1060 p = (unsigned char *) &n;
1061 b = c->oobbuf + c->fsdata_pos;
1062
1063 for (i = c->fsdata_len; i; i--) {
1064 if (*b++ != *p++)
1065 ret = 1;
1066 }
1067
1068 D1(if (ret == 1) {
1069 printk(KERN_WARNING "jffs2_check_nand_cleanmarker(): "
1070 "Cleanmarker node not detected in block at %08x\n",
1071 offset);
1072 printk(KERN_WARNING "OOB at %08zx was ", offset);
1073 for (i=0; i < oobsize; i++)
1074 printk("%02x ", c->oobbuf[i]);
1075 printk("\n");
1076 });
1077 return ret;
1089} 1078}
1090 1079
1091int jffs2_write_nand_cleanmarker(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) 1080int jffs2_write_nand_cleanmarker(struct jffs2_sb_info *c,
1081 struct jffs2_eraseblock *jeb)
1092{ 1082{
1093 struct jffs2_unknown_node n; 1083 struct jffs2_unknown_node n;
1094 int ret; 1084 int ret;
1095 size_t retlen; 1085 struct mtd_oob_ops ops;
1096 1086
1097 n.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); 1087 n.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
1098 n.nodetype = cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER); 1088 n.nodetype = cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER);
1099 n.totlen = cpu_to_je32(8); 1089 n.totlen = cpu_to_je32(8);
1100 1090
1101 ret = jffs2_flash_write_oob(c, jeb->offset + c->fsdata_pos, c->fsdata_len, &retlen, (unsigned char *)&n); 1091 ops.len = c->fsdata_len;
1092 ops.ooblen = c->fsdata_len;;
1093 ops.oobbuf = (uint8_t *)&n;
1094 ops.ooboffs = c->fsdata_pos;
1095 ops.datbuf = NULL;
1096 ops.mode = MTD_OOB_PLACE;
1097
1098 ret = c->mtd->write_oob(c->mtd, jeb->offset, &ops);
1102 1099
1103 if (ret) { 1100 if (ret) {
1104 D1(printk(KERN_WARNING "jffs2_write_nand_cleanmarker(): Write failed for block at %08x: error %d\n", jeb->offset, ret)); 1101 D1(printk(KERN_WARNING "jffs2_write_nand_cleanmarker(): "
1102 "Write failed for block at %08x: error %d\n",
1103 jeb->offset, ret));
1105 return ret; 1104 return ret;
1106 } 1105 }
1107 if (retlen != c->fsdata_len) { 1106 if (ops.retlen != ops.len) {
1108 D1(printk(KERN_WARNING "jffs2_write_nand_cleanmarker(): Short write for block at %08x: %zd not %d\n", jeb->offset, retlen, c->fsdata_len)); 1107 D1(printk(KERN_WARNING "jffs2_write_nand_cleanmarker(): "
1109 return ret; 1108 "Short write for block at %08x: %zd not %d\n",
1109 jeb->offset, ops.retlen, ops.len));
1110 return -EIO;
1110 } 1111 }
1111 return 0; 1112 return 0;
1112} 1113}
@@ -1185,6 +1186,10 @@ int jffs2_nand_flash_setup(struct jffs2_sb_info *c)
1185 if (!c->wbuf) 1186 if (!c->wbuf)
1186 return -ENOMEM; 1187 return -ENOMEM;
1187 1188
1189 c->oobbuf = kmalloc(NR_OOB_SCAN_PAGES * c->mtd->oobsize, GFP_KERNEL);
1190 if (!c->oobbuf)
1191 return -ENOMEM;
1192
1188 res = jffs2_nand_set_oobinfo(c); 1193 res = jffs2_nand_set_oobinfo(c);
1189 1194
1190#ifdef BREAKME 1195#ifdef BREAKME
@@ -1202,6 +1207,7 @@ int jffs2_nand_flash_setup(struct jffs2_sb_info *c)
1202void jffs2_nand_flash_cleanup(struct jffs2_sb_info *c) 1207void jffs2_nand_flash_cleanup(struct jffs2_sb_info *c)
1203{ 1208{
1204 kfree(c->wbuf); 1209 kfree(c->wbuf);
1210 kfree(c->oobbuf);
1205} 1211}
1206 1212
1207int jffs2_dataflash_setup(struct jffs2_sb_info *c) { 1213int jffs2_dataflash_setup(struct jffs2_sb_info *c) {