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 195 buffer = yaffs_get_temp_buffer(dev); 196 n_bytes = sizeof(struct yaffs_summary_tags) * dev->chunks_per_summary; 197 chunk_in_block = dev->chunks_per_summary; 198 chunk_in_nand = blk * dev->param.chunks_per_block + 199 dev->chunks_per_summary; 200 chunk_id = 1; 201 do { 202 this_tx = n_bytes; 203 if (this_tx > sum_bytes_per_chunk) 204 this_tx = sum_bytes_per_chunk; 205 result = yaffs_rd_chunk_tags_nand(dev, chunk_in_nand, 206 buffer, &tags); 207 208 if (tags.chunk_id != chunk_id || 209 tags.obj_id != YAFFS_OBJECTID_SUMMARY || 210 tags.chunk_used == 0 || 211 tags.ecc_result > YAFFS_ECC_RESULT_FIXED || 212 tags.n_bytes != (this_tx + sizeof(hdr))) 213 result = YAFFS_FAIL; 214 if (result != YAFFS_OK) 215 break; 216 217 if (st == dev->sum_tags) { 218 /* If we're scanning then update the block info */ 219 yaffs_set_chunk_bit(dev, blk, chunk_in_block); 220 bi->pages_in_use++; 221 } 222 memcpy(&hdr, buffer, sizeof(hdr)); 223 memcpy(sum_buffer, buffer + sizeof(hdr), this_tx); 224 n_bytes -= this_tx; 225 sum_buffer += this_tx; 226 chunk_in_nand++; 227 chunk_in_block++; 228 chunk_id++; 229 } while (result == YAFFS_OK && n_bytes > 0); 230 yaffs_release_temp_buffer(dev, buffer); 231 232 if (result == YAFFS_OK) { 233 /* Verify header */ 234 if (hdr.version != YAFFS_SUMMARY_VERSION || 235 hdr.block != blk || 236 hdr.seq != bi->seq_number || 237 hdr.sum != yaffs_summary_sum(dev)) 238 result = YAFFS_FAIL; 239 } 240 241 if (st == dev->sum_tags && result == YAFFS_OK) 242 bi->has_summary = 1; 243 244 return result; 245 } 246 247 int yaffs_summary_add(struct yaffs_dev *dev, 248 struct yaffs_ext_tags *tags, 249 int chunk_in_nand) 250 { 251 struct yaffs_packed_tags2_tags_only tags_only; 252 struct yaffs_summary_tags *sum_tags; 253 int block_in_nand = chunk_in_nand / dev->param.chunks_per_block; 254 int chunk_in_block = chunk_in_nand % dev->param.chunks_per_block; 255 256 if (!dev->sum_tags) 257 return YAFFS_OK; 258 259 if (chunk_in_block >= 0 && chunk_in_block < dev->chunks_per_summary) { 260 yaffs_pack_tags2_tags_only(&tags_only, tags); 261 sum_tags = &dev->sum_tags[chunk_in_block]; 262 sum_tags->chunk_id = tags_only.chunk_id; 263 sum_tags->n_bytes = tags_only.n_bytes; 264 sum_tags->obj_id = tags_only.obj_id; 265 266 if (chunk_in_block == dev->chunks_per_summary - 1) { 267 /* Time to write out the summary */ 268 yaffs_summary_write(dev, block_in_nand); 269 yaffs_summary_clear(dev); 270 yaffs_skip_rest_of_block(dev); 271 } 272 } 273 return YAFFS_OK; 274 } 275 276 int yaffs_summary_fetch(struct yaffs_dev *dev, 277 struct yaffs_ext_tags *tags, 278 int chunk_in_block) 279 { 280 struct yaffs_packed_tags2_tags_only tags_only; 281 struct yaffs_summary_tags *sum_tags; 282 if (chunk_in_block >= 0 && chunk_in_block < dev->chunks_per_summary) { 283 sum_tags = &dev->sum_tags[chunk_in_block]; 284 tags_only.chunk_id = sum_tags->chunk_id; 285 tags_only.n_bytes = sum_tags->n_bytes; 286 tags_only.obj_id = sum_tags->obj_id; 287 yaffs_unpack_tags2_tags_only(tags, &tags_only); 288 return YAFFS_OK; 289 } 290 return YAFFS_FAIL; 291 } 292 293 void yaffs_summary_gc(struct yaffs_dev *dev, int blk) 294 { 295 struct yaffs_block_info *bi = yaffs_get_block_info(dev, blk); 296 int i; 297 298 if (!bi->has_summary) 299 return; 300 301 for (i = dev->chunks_per_summary; 302 i < dev->param.chunks_per_block; 303 i++) { 304 if (yaffs_check_chunk_bit(dev, blk, i)) { 305 yaffs_clear_chunk_bit(dev, blk, i); 306 bi->pages_in_use--; 307 dev->n_free_chunks++; 308 } 309 } 310 } 311