diff options
Diffstat (limited to 'fs/jffs2/wbuf.c')
-rw-r--r-- | fs/jffs2/wbuf.c | 230 |
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 | */ |
961 | int jffs2_check_oob_empty( struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, int mode) | 963 | int 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; | |
1018 | out: | ||
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 | 1017 | int jffs2_check_nand_cleanmarker (struct jffs2_sb_info *c, |
1028 | * physical blocks | 1018 | struct jffs2_eraseblock *jeb) |
1029 | */ | ||
1030 | int 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 | ||
1091 | int jffs2_write_nand_cleanmarker(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) | 1080 | int 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) | |||
1202 | void jffs2_nand_flash_cleanup(struct jffs2_sb_info *c) | 1207 | void 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 | ||
1207 | int jffs2_dataflash_setup(struct jffs2_sb_info *c) { | 1213 | int jffs2_dataflash_setup(struct jffs2_sb_info *c) { |