diff options
author | Artem Bityutskiy <Artem.Bityutskiy@nokia.com> | 2007-01-31 04:38:53 -0500 |
---|---|---|
committer | David Woodhouse <dwmw2@infradead.org> | 2007-02-09 10:34:08 -0500 |
commit | a7a6ace1406f95c3edb8365788f85984377f3832 (patch) | |
tree | 852a51608c1e2f437786750ca326ee8ce4b61364 /fs/jffs2/wbuf.c | |
parent | 73a4421c5a0aa77b996891e7616b396c360b3ed8 (diff) |
[JFFS2] Use MTD_OOB_AUTO to automatically place cleanmarker on NAND
Nowadays MTD supports an MTD_OOB_AUTO option which allows users
to access free bytes in NAND's OOB as a contiguous buffer, although
it may be highly discontinuous.
This patch teaches JFFS2 to use this nice feature instead of the
old MTD_OOB_PLACE option. This for example caused problems with
OneNAND. Now JFFS2 does not care how are the free bytes situated.
This may change position of the clean marker on some flashes,
but this is not a problem. JFFS2 will just re-erase the empty
eraseblocks and write the new (correct) clean marker.
Signed-off-by: Artem Bityutskiy <Artem.Bityutskiy@nokia.com>
Signed-off-by: David Woodhouse <dwmw2@infradead.org>
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) |