diff options
Diffstat (limited to 'fs/jffs2/wbuf.c')
-rw-r--r-- | fs/jffs2/wbuf.c | 203 |
1 files changed, 67 insertions, 136 deletions
diff --git a/fs/jffs2/wbuf.c b/fs/jffs2/wbuf.c index 9c99859f5edd..58cb77baed8e 100644 --- a/fs/jffs2/wbuf.c +++ b/fs/jffs2/wbuf.c | |||
@@ -957,43 +957,48 @@ exit: | |||
957 | return ret; | 957 | return ret; |
958 | } | 958 | } |
959 | 959 | ||
960 | #define NR_OOB_SCAN_PAGES 4 | 960 | #define NR_OOB_SCAN_PAGES 4 |
961 | |||
962 | /* For historical reasons we use only 12 bytes for OOB clean marker */ | ||
963 | #define OOB_CM_SIZE 12 | ||
964 | |||
965 | static const struct jffs2_unknown_node oob_cleanmarker = | ||
966 | { | ||
967 | .magic = cpu_to_je16(JFFS2_MAGIC_BITMASK), | ||
968 | .nodetype = cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER), | ||
969 | .totlen = cpu_to_je32(8) | ||
970 | }; | ||
961 | 971 | ||
962 | /* | 972 | /* |
963 | * Check, if the out of band area is empty | 973 | * Check, if the out of band area is empty. This function knows about the clean |
974 | * marker and if it is present in OOB, treats the OOB as empty anyway. | ||
964 | */ | 975 | */ |
965 | int jffs2_check_oob_empty(struct jffs2_sb_info *c, | 976 | int jffs2_check_oob_empty(struct jffs2_sb_info *c, |
966 | struct jffs2_eraseblock *jeb, int mode) | 977 | struct jffs2_eraseblock *jeb, int mode) |
967 | { | 978 | { |
968 | int i, page, ret; | 979 | int i, ret; |
969 | int oobsize = c->mtd->oobsize; | 980 | int cmlen = min_t(int, c->oobavail, OOB_CM_SIZE); |
970 | struct mtd_oob_ops ops; | 981 | struct mtd_oob_ops ops; |
971 | 982 | ||
972 | ops.ooblen = NR_OOB_SCAN_PAGES * oobsize; | 983 | ops.mode = MTD_OOB_AUTO; |
984 | ops.ooblen = NR_OOB_SCAN_PAGES * c->oobavail; | ||
973 | ops.oobbuf = c->oobbuf; | 985 | ops.oobbuf = c->oobbuf; |
974 | ops.ooboffs = 0; | 986 | ops.len = ops.ooboffs = ops.retlen = ops.oobretlen = 0; |
975 | ops.datbuf = NULL; | 987 | ops.datbuf = NULL; |
976 | ops.mode = MTD_OOB_PLACE; | ||
977 | 988 | ||
978 | ret = c->mtd->read_oob(c->mtd, jeb->offset, &ops); | 989 | ret = c->mtd->read_oob(c->mtd, jeb->offset, &ops); |
979 | if (ret) { | 990 | if (ret || ops.oobretlen != ops.ooblen) { |
980 | D1(printk(KERN_WARNING "jffs2_check_oob_empty(): Read OOB " | 991 | printk(KERN_ERR "cannot read OOB for EB at %08x, requested %d " |
981 | "failed %d for block at %08x\n", ret, jeb->offset)); | 992 | "bytes, read %d bytes, error %d\n", jeb->offset, |
993 | ops.ooblen, ops.oobretlen, ret); | ||
994 | if (!ret) | ||
995 | ret = -EIO; | ||
982 | return ret; | 996 | return ret; |
983 | } | 997 | } |
984 | 998 | ||
985 | if (ops.oobretlen < ops.ooblen) { | 999 | for(i = 0; i < ops.ooblen; i++) { |
986 | D1(printk(KERN_WARNING "jffs2_check_oob_empty(): Read OOB " | 1000 | if (mode && i < cmlen) |
987 | "returned short read (%zd bytes not %d) for block " | 1001 | /* Yeah, we know about the cleanmarker */ |
988 | "at %08x\n", ops.oobretlen, ops.ooblen, jeb->offset)); | ||
989 | return -EIO; | ||
990 | } | ||
991 | |||
992 | /* Special check for first page */ | ||
993 | for(i = 0; i < oobsize ; i++) { | ||
994 | /* Yeah, we know about the cleanmarker. */ | ||
995 | if (mode && i >= c->fsdata_pos && | ||
996 | i < c->fsdata_pos + c->fsdata_len) | ||
997 | continue; | 1002 | continue; |
998 | 1003 | ||
999 | if (ops.oobbuf[i] != 0xFF) { | 1004 | if (ops.oobbuf[i] != 0xFF) { |
@@ -1003,111 +1008,63 @@ int jffs2_check_oob_empty(struct jffs2_sb_info *c, | |||
1003 | } | 1008 | } |
1004 | } | 1009 | } |
1005 | 1010 | ||
1006 | /* we know, we are aligned :) */ | ||
1007 | for (page = oobsize; page < ops.ooblen; page += sizeof(long)) { | ||
1008 | long dat = *(long *)(&ops.oobbuf[page]); | ||
1009 | if(dat != -1) | ||
1010 | return 1; | ||
1011 | } | ||
1012 | return 0; | 1011 | return 0; |
1013 | } | 1012 | } |
1014 | 1013 | ||
1015 | /* | 1014 | /* |
1016 | * Scan for a valid cleanmarker and for bad blocks | 1015 | * Check for a valid cleanmarker. |
1016 | * Returns: 0 if a valid cleanmarker was found | ||
1017 | * 1 if no cleanmarker was found | ||
1018 | * negative error code if an error occurred | ||
1017 | */ | 1019 | */ |
1018 | int jffs2_check_nand_cleanmarker (struct jffs2_sb_info *c, | 1020 | int jffs2_check_nand_cleanmarker(struct jffs2_sb_info *c, |
1019 | struct jffs2_eraseblock *jeb) | 1021 | struct jffs2_eraseblock *jeb) |
1020 | { | 1022 | { |
1021 | struct jffs2_unknown_node n; | ||
1022 | struct mtd_oob_ops ops; | 1023 | struct mtd_oob_ops ops; |
1023 | int oobsize = c->mtd->oobsize; | 1024 | int ret, cmlen = min_t(int, c->oobavail, OOB_CM_SIZE); |
1024 | unsigned char *p,*b; | ||
1025 | int i, ret; | ||
1026 | size_t offset = jeb->offset; | ||
1027 | |||
1028 | /* Check first if the block is bad. */ | ||
1029 | if (c->mtd->block_isbad(c->mtd, offset)) { | ||
1030 | D1 (printk(KERN_WARNING "jffs2_check_nand_cleanmarker()" | ||
1031 | ": Bad block at %08x\n", jeb->offset)); | ||
1032 | return 2; | ||
1033 | } | ||
1034 | 1025 | ||
1035 | ops.ooblen = oobsize; | 1026 | ops.mode = MTD_OOB_AUTO; |
1027 | ops.ooblen = cmlen; | ||
1036 | ops.oobbuf = c->oobbuf; | 1028 | ops.oobbuf = c->oobbuf; |
1037 | ops.ooboffs = 0; | 1029 | ops.len = ops.ooboffs = ops.retlen = ops.oobretlen = 0; |
1038 | ops.datbuf = NULL; | 1030 | ops.datbuf = NULL; |
1039 | ops.mode = MTD_OOB_PLACE; | ||
1040 | 1031 | ||
1041 | ret = c->mtd->read_oob(c->mtd, offset, &ops); | 1032 | ret = c->mtd->read_oob(c->mtd, jeb->offset, &ops); |
1042 | if (ret) { | 1033 | if (ret || ops.oobretlen != ops.ooblen) { |
1043 | D1 (printk(KERN_WARNING "jffs2_check_nand_cleanmarker(): " | 1034 | printk(KERN_ERR "cannot read OOB for EB at %08x, requested %d " |
1044 | "Read OOB failed %d for block at %08x\n", | 1035 | "bytes, read %d bytes, error %d\n", jeb->offset, |
1045 | ret, jeb->offset)); | 1036 | ops.ooblen, ops.oobretlen, ret); |
1037 | if (!ret) | ||
1038 | ret = -EIO; | ||
1046 | return ret; | 1039 | return ret; |
1047 | } | 1040 | } |
1048 | 1041 | ||
1049 | if (ops.oobretlen < ops.ooblen) { | 1042 | return !!memcmp(&oob_cleanmarker, c->oobbuf, cmlen); |
1050 | D1 (printk (KERN_WARNING "jffs2_check_nand_cleanmarker(): " | ||
1051 | "Read OOB return short read (%zd bytes not %d) " | ||
1052 | "for block at %08x\n", ops.oobretlen, ops.ooblen, | ||
1053 | jeb->offset)); | ||
1054 | return -EIO; | ||
1055 | } | ||
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; | ||
1078 | } | 1043 | } |
1079 | 1044 | ||
1080 | int jffs2_write_nand_cleanmarker(struct jffs2_sb_info *c, | 1045 | int jffs2_write_nand_cleanmarker(struct jffs2_sb_info *c, |
1081 | struct jffs2_eraseblock *jeb) | 1046 | struct jffs2_eraseblock *jeb) |
1082 | { | 1047 | { |
1083 | struct jffs2_unknown_node n; | 1048 | int ret; |
1084 | int ret; | ||
1085 | struct mtd_oob_ops ops; | 1049 | struct mtd_oob_ops ops; |
1050 | int cmlen = min_t(int, c->oobavail, OOB_CM_SIZE); | ||
1086 | 1051 | ||
1087 | n.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); | 1052 | ops.mode = MTD_OOB_AUTO; |
1088 | n.nodetype = cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER); | 1053 | ops.ooblen = cmlen; |
1089 | n.totlen = cpu_to_je32(8); | 1054 | ops.oobbuf = (uint8_t *)&oob_cleanmarker; |
1090 | 1055 | ops.len = ops.ooboffs = ops.retlen = ops.oobretlen = 0; | |
1091 | ops.ooblen = c->fsdata_len; | ||
1092 | ops.oobbuf = (uint8_t *)&n; | ||
1093 | ops.ooboffs = c->fsdata_pos; | ||
1094 | ops.datbuf = NULL; | 1056 | ops.datbuf = NULL; |
1095 | ops.mode = MTD_OOB_PLACE; | ||
1096 | 1057 | ||
1097 | ret = c->mtd->write_oob(c->mtd, jeb->offset, &ops); | 1058 | ret = c->mtd->write_oob(c->mtd, jeb->offset, &ops); |
1098 | 1059 | if (ret || ops.oobretlen != ops.ooblen) { | |
1099 | if (ret) { | 1060 | printk(KERN_ERR "cannot write OOB for EB at %08x, requested %d " |
1100 | D1(printk(KERN_WARNING "jffs2_write_nand_cleanmarker(): " | 1061 | "bytes, read %d bytes, error %d\n", jeb->offset, |
1101 | "Write failed for block at %08x: error %d\n", | 1062 | ops.ooblen, ops.oobretlen, ret); |
1102 | jeb->offset, ret)); | 1063 | if (!ret) |
1064 | ret = -EIO; | ||
1103 | return ret; | 1065 | return ret; |
1104 | } | 1066 | } |
1105 | if (ops.oobretlen != ops.ooblen) { | 1067 | |
1106 | D1(printk(KERN_WARNING "jffs2_write_nand_cleanmarker(): " | ||
1107 | "Short write for block at %08x: %zd not %d\n", | ||
1108 | jeb->offset, ops.oobretlen, ops.ooblen)); | ||
1109 | return -EIO; | ||
1110 | } | ||
1111 | return 0; | 1068 | return 0; |
1112 | } | 1069 | } |
1113 | 1070 | ||
@@ -1140,41 +1097,24 @@ int jffs2_write_nand_badblock(struct jffs2_sb_info *c, struct jffs2_eraseblock * | |||
1140 | return 1; | 1097 | return 1; |
1141 | } | 1098 | } |
1142 | 1099 | ||
1143 | static int jffs2_nand_set_oobinfo(struct jffs2_sb_info *c) | 1100 | int jffs2_nand_flash_setup(struct jffs2_sb_info *c) |
1144 | { | 1101 | { |
1145 | struct nand_ecclayout *oinfo = c->mtd->ecclayout; | 1102 | struct nand_ecclayout *oinfo = c->mtd->ecclayout; |
1146 | 1103 | ||
1147 | /* Do this only, if we have an oob buffer */ | ||
1148 | if (!c->mtd->oobsize) | 1104 | if (!c->mtd->oobsize) |
1149 | return 0; | 1105 | return 0; |
1150 | 1106 | ||
1151 | /* Cleanmarker is out-of-band, so inline size zero */ | 1107 | /* Cleanmarker is out-of-band, so inline size zero */ |
1152 | c->cleanmarker_size = 0; | 1108 | c->cleanmarker_size = 0; |
1153 | 1109 | ||
1154 | /* Should we use autoplacement ? */ | 1110 | if (!oinfo || oinfo->oobavail == 0) { |
1155 | if (!oinfo) { | 1111 | printk(KERN_ERR "inconsistent device description\n"); |
1156 | D1(printk(KERN_DEBUG "JFFS2 on NAND. No autoplacment info found\n")); | ||
1157 | return -EINVAL; | 1112 | return -EINVAL; |
1158 | } | 1113 | } |
1159 | 1114 | ||
1160 | D1(printk(KERN_DEBUG "JFFS2 using autoplace on NAND\n")); | 1115 | D1(printk(KERN_DEBUG "JFFS2 using OOB on NAND\n")); |
1161 | /* Get the position of the free bytes */ | ||
1162 | if (!oinfo->oobfree[0].length) { | ||
1163 | printk (KERN_WARNING "jffs2_nand_set_oobinfo(): Eeep." | ||
1164 | " Autoplacement selected and no empty space in oob\n"); | ||
1165 | return -ENOSPC; | ||
1166 | } | ||
1167 | c->fsdata_pos = oinfo->oobfree[0].offset; | ||
1168 | c->fsdata_len = oinfo->oobfree[0].length; | ||
1169 | if (c->fsdata_len > 8) | ||
1170 | c->fsdata_len = 8; | ||
1171 | 1116 | ||
1172 | return 0; | 1117 | c->oobavail = oinfo->oobavail; |
1173 | } | ||
1174 | |||
1175 | int jffs2_nand_flash_setup(struct jffs2_sb_info *c) | ||
1176 | { | ||
1177 | int res; | ||
1178 | 1118 | ||
1179 | /* Initialise write buffer */ | 1119 | /* Initialise write buffer */ |
1180 | init_rwsem(&c->wbuf_sem); | 1120 | init_rwsem(&c->wbuf_sem); |
@@ -1185,22 +1125,13 @@ int jffs2_nand_flash_setup(struct jffs2_sb_info *c) | |||
1185 | if (!c->wbuf) | 1125 | if (!c->wbuf) |
1186 | return -ENOMEM; | 1126 | return -ENOMEM; |
1187 | 1127 | ||
1188 | c->oobbuf = kmalloc(NR_OOB_SCAN_PAGES * c->mtd->oobsize, GFP_KERNEL); | 1128 | c->oobbuf = kmalloc(NR_OOB_SCAN_PAGES * c->oobavail, GFP_KERNEL); |
1189 | if (!c->oobbuf) | 1129 | if (!c->oobbuf) { |
1190 | return -ENOMEM; | ||
1191 | |||
1192 | res = jffs2_nand_set_oobinfo(c); | ||
1193 | |||
1194 | #ifdef BREAKME | ||
1195 | if (!brokenbuf) | ||
1196 | brokenbuf = kmalloc(c->wbuf_pagesize, GFP_KERNEL); | ||
1197 | if (!brokenbuf) { | ||
1198 | kfree(c->wbuf); | 1130 | kfree(c->wbuf); |
1199 | return -ENOMEM; | 1131 | return -ENOMEM; |
1200 | } | 1132 | } |
1201 | memset(brokenbuf, 0xdb, c->wbuf_pagesize); | 1133 | |
1202 | #endif | 1134 | return 0; |
1203 | return res; | ||
1204 | } | 1135 | } |
1205 | 1136 | ||
1206 | void jffs2_nand_flash_cleanup(struct jffs2_sb_info *c) | 1137 | void jffs2_nand_flash_cleanup(struct jffs2_sb_info *c) |