1 /* 2 * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. 3 * 4 * Copyright (C) 2002-2011 Aleph One Ltd. 5 * for Toby Churchill Ltd and Brightstar Engineering 6 * 7 * Created by Charles Manning <charles@aleph1.co.uk> 8 * 9 * This program is free software; you can redistribute it and/or modify 10 * it under the terms of the GNU General Public License version 2 as 11 * published by the Free Software Foundation. 12 */ 13 14 /* Summaries write the useful part of the tags for the chunks in a block into an 15 * an array which is written to the last n chunks of the block. 16 * Reading the summaries gives all the tags for the block in one read. Much 17 * faster. 18 * 19 * Chunks holding summaries are marked with tags making it look like 20 * they are part of a fake file. 21 * 22 * The summary could also be used during gc. 23 * 24 */ 25 26 #include "yaffs_summary.h" 27 #include "yaffs_packedtags2.h" 28 #include "yaffs_nand.h" 29 #include "yaffs_getblockinfo.h" 30 #include "yaffs_bitmap.h" 31 32 /* 33 * The summary is built up in an array of summary tags. 34 * This gets written to the last one or two (maybe more) chunks in a block. 35 * A summary header is written as the first part of each chunk of summary data. 36 * The summary header must match or the summary is rejected. 37 */ 38 39 /* Summary tags don't need the sequence number because that is redundant. */ 40 struct yaffs_summary_tags { 41 unsigned obj_id; 42 unsigned chunk_id; 43 unsigned n_bytes; 44 }; 45 46 /* Summary header */ 47 struct yaffs_summary_header { 48 unsigned version; /* Must match current version */ 49 unsigned block; /* Must be this block */ 50 unsigned seq; /* Must be this sequence number */ 51 unsigned sum; /* Just add up all the bytes in the tags */ 52 }; 53 54 55 static void yaffs_summary_clear(struct yaffs_dev *dev) 56 { 57 if (!dev->sum_tags) 58 return; 59 memset(dev->sum_tags, 0, dev->chunks_per_summary * 60 sizeof(struct yaffs_summary_tags)); 61 } 62 63 64 void yaffs_summary_deinit(struct yaffs_dev *dev) 65 { 66 kfree(dev->sum_tags); 67 dev->sum_tags = NULL; 68 kfree(dev->gc_sum_tags); 69 dev->gc_sum_tags = NULL; 70 dev->chunks_per_summary = 0; 71 } 72 73 int yaffs_summary_init(struct yaffs_dev *dev) 74 { 75 int sum_bytes; 76 int chunks_used; /* Number of chunks used by summary */ 77 int sum_tags_bytes; 78 79 sum_bytes = dev->param.chunks_per_block * 80 sizeof(struct yaffs_summary_tags); 81 82 chunks_used = (sum_bytes + dev->data_bytes_per_chunk - 1)/ 83 (dev->data_bytes_per_chunk - 84 sizeof(struct yaffs_summary_header)); 85 86 dev->chunks_per_summary = dev->param.chunks_per_block - chunks_used; 87 sum_tags_bytes = sizeof(struct yaffs_summary_tags) * 88 dev->chunks_per_summary; 89 dev->sum_tags = kmalloc(sum_tags_bytes, GFP_NOFS); 90 dev->gc_sum_tags = kmalloc(sum_tags_bytes, GFP_NOFS); 91 if (!dev->sum_tags || !dev->gc_sum_tags) { 92 yaffs_summary_deinit(dev); 93 return YAFFS_FAIL; 94 } 95 96 yaffs_summary_clear(dev); 97 98 return YAFFS_OK; 99 } 100 101 static unsigned yaffs_summary_sum(struct yaffs_dev *dev) 102 { 103 u8 *sum_buffer = (u8 *)dev->sum_tags; 104 int i; 105 unsigned sum = 0; 106 107 i = sizeof(struct yaffs_summary_tags) * 108 dev->chunks_per_summary; 109 while (i > 0) { 110 sum += *sum_buffer; 111 sum_buffer++; 112 i--; 113 } 114 115 return sum; 116 } 117 118 static int yaffs_summary_write(struct yaffs_dev *dev, int blk) 119 { 120 struct yaffs_ext_tags tags; 121 u8 *buffer; 122 u8 *sum_buffer = (u8 *)dev->sum_tags; 123 int n_bytes; 124 int chunk_in_nand; 125 int chunk_in_block; 126 int result; 127 int this_tx; 128 struct yaffs_summary_header hdr; 129 int sum_bytes_per_chunk = dev->data_bytes_per_chunk - sizeof(hdr); 130 struct yaffs_block_info *bi = yaffs_get_block_info(dev, blk); 131 132 buffer = yaffs_get_temp_buffer(dev); 133 n_bytes = sizeof(struct yaffs_summary_tags) * 134 dev->chunks_per_summary; 135 memset(&tags, 0, sizeof(struct yaffs_ext_tags)); 136 tags.obj_id = YAFFS_OBJECTID_SUMMARY; 137 tags.chunk_id = 1; 138 chunk_in_block = dev->chunks_per_summary; 139 chunk_in_nand = dev->alloc_block * dev->param.chunks_per_block + 140 dev->chunks_per_summary; 141 hdr.version = YAFFS_SUMMARY_VERSION; 142 hdr.block = blk; 143 hdr.seq = bi->seq_number; 144 hdr.sum = yaffs_summary_sum(dev); 145 146 do { 147 this_tx = n_bytes; 148 if (this_tx > sum_bytes_per_chunk) 149 this_tx = sum_bytes_per_chunk; 150 memcpy(buffer, &hdr, sizeof(hdr)); 151 memcpy(buffer + sizeof(hdr), sum_buffer, this_tx); 152 tags.n_bytes = this_tx + sizeof(hdr); 153 result = yaffs_wr_chunk_tags_nand(dev, chunk_in_nand, 154 buffer, &tags); 155 156 if (result != YAFFS_OK) 157 break; 158 yaffs_set_chunk_bit(dev, blk, chunk_in_block); 159 bi->pages_in_use++; 160 dev->n_free_chunks--; 161 162 n_bytes -= this_tx; 163 sum_buffer += this_tx; 164 chunk_in_nand++; 165 chunk_in_block++; 166 tags.chunk_id++; 167 } while (result == YAFFS_OK && n_bytes > 0); 168 yaffs_release_temp_buffer(dev, buffer); 169 170 171 if (result == YAFFS_OK) 172 bi->has_summary = 1; 173 174 175 return result; 176 } 177 178 int yaffs_summary_read(struct yaffs_dev *dev, 179 struct yaffs_summary_tags *st, 180 int blk) 181 { 182 struct yaffs_ext_tags tags; 183 u8 *buffer; 184 u8 *sum_buffer = (u8 *)st; 185 int n_bytes; 186 int chunk_id; 187 int chunk_in_nand; 188 int chunk_in_block; 189 int result; 190 int this_tx; 191 struct yaffs_summary_header hdr; 192 struct yaffs_block_info *bi = yaffs_get_block_info(dev, blk); 193 int sum_bytes_per_chunk = dev->data_bytes_per_chunk - sizeof(hdr); 194 int sum_tags_bytes; 195 196 sum_tags_bytes = sizeof(struct yaffs_summary_tags) * 197 dev->chunks_per_summary; 198 buffer = yaffs_get_temp_buffer(dev); 199 n_bytes = sizeof(struct yaffs_summary_tags) * dev->chunks_per_summary; 200 chunk_in_block = dev->chunks_per_summary; 201 chunk_in_nand = blk * dev->param.chunks_per_block + 202 dev->chunks_per_summary; 203 chunk_id = 1; 204 do { 205 this_tx = n_bytes; 206 if (this_tx > sum_bytes_per_chunk) 207 this_tx = sum_bytes_per_chunk; 208 result = yaffs_rd_chunk_tags_nand(dev, chunk_in_nand, 209 buffer, &tags); 210 211 if (tags.chunk_id != chunk_id || 212 tags.obj_id != YAFFS_OBJECTID_SUMMARY || 213 tags.chunk_used == 0 || 214 tags.ecc_result > YAFFS_ECC_RESULT_FIXED || 215 tags.n_bytes != (this_tx + sizeof(hdr))) 216 result = YAFFS_FAIL; 217 if (result != YAFFS_OK) 218 break; 219 220 if (st == dev->sum_tags) { 221 /* If we're scanning then update the block info */ 222 yaffs_set_chunk_bit(dev, blk, chunk_in_block); 223 bi->pages_in_use++; 224 } 225 memcpy(&hdr, buffer, sizeof(hdr)); 226 memcpy(sum_buffer, buffer + sizeof(hdr), this_tx); 227 n_bytes -= this_tx; 228 sum_buffer += this_tx; 229 chunk_in_nand++; 230 chunk_in_block++; 231 chunk_id++; 232 } while (result == YAFFS_OK && n_bytes > 0); 233 yaffs_release_temp_buffer(dev, buffer); 234 235 if (result == YAFFS_OK) { 236 /* Verify header */ 237 if (hdr.version != YAFFS_SUMMARY_VERSION || 238 hdr.block != blk || 239 hdr.seq != bi->seq_number || 240 hdr.sum != yaffs_summary_sum(dev)) 241 result = YAFFS_FAIL; 242 } 243 244 if (st == dev->sum_tags && result == YAFFS_OK) 245 bi->has_summary = 1; 246 247 return result; 248 } 249 250 int yaffs_summary_add(struct yaffs_dev *dev, 251 struct yaffs_ext_tags *tags, 252 int chunk_in_nand) 253 { 254 struct yaffs_packed_tags2_tags_only tags_only; 255 struct yaffs_summary_tags *sum_tags; 256 int block_in_nand = chunk_in_nand / dev->param.chunks_per_block; 257 int chunk_in_block = chunk_in_nand % dev->param.chunks_per_block; 258 259 if (!dev->sum_tags) 260 return YAFFS_OK; 261 262 if (chunk_in_block >= 0 && chunk_in_block < dev->chunks_per_summary) { 263 yaffs_pack_tags2_tags_only(&tags_only, tags); 264 sum_tags = &dev->sum_tags[chunk_in_block]; 265 sum_tags->chunk_id = tags_only.chunk_id; 266 sum_tags->n_bytes = tags_only.n_bytes; 267 sum_tags->obj_id = tags_only.obj_id; 268 269 if (chunk_in_block == dev->chunks_per_summary - 1) { 270 /* Time to write out the summary */ 271 yaffs_summary_write(dev, block_in_nand); 272 yaffs_summary_clear(dev); 273 yaffs_skip_rest_of_block(dev); 274 } 275 } 276 return YAFFS_OK; 277 } 278 279 int yaffs_summary_fetch(struct yaffs_dev *dev, 280 struct yaffs_ext_tags *tags, 281 int chunk_in_block) 282 { 283 struct yaffs_packed_tags2_tags_only tags_only; 284 struct yaffs_summary_tags *sum_tags; 285 if (chunk_in_block >= 0 && chunk_in_block < dev->chunks_per_summary) { 286 sum_tags = &dev->sum_tags[chunk_in_block]; 287 tags_only.chunk_id = sum_tags->chunk_id; 288 tags_only.n_bytes = sum_tags->n_bytes; 289 tags_only.obj_id = sum_tags->obj_id; 290 yaffs_unpack_tags2_tags_only(tags, &tags_only); 291 return YAFFS_OK; 292 } 293 return YAFFS_FAIL; 294 } 295 296 void yaffs_summary_gc(struct yaffs_dev *dev, int blk) 297 { 298 struct yaffs_block_info *bi = yaffs_get_block_info(dev, blk); 299 int i; 300 301 if (!bi->has_summary) 302 return; 303 304 for (i = dev->chunks_per_summary; 305 i < dev->param.chunks_per_block; 306 i++) { 307 if (yaffs_check_chunk_bit(dev, blk, i)) { 308 yaffs_clear_chunk_bit(dev, blk, i); 309 bi->pages_in_use--; 310 dev->n_free_chunks++; 311 } 312 } 313 } 314