uboot/fs/yaffs2/yaffs_summary.c
<<
>>
Prefs
   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. */
  40struct yaffs_summary_tags {
  41        unsigned obj_id;
  42        unsigned chunk_id;
  43        unsigned n_bytes;
  44};
  45
  46/* Summary header */
  47struct 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
  55static 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
  64void 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
  73int 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
 101static 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
 118static 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
 178int 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.seq != bi->seq_number ||
 236                    hdr.sum != yaffs_summary_sum(dev))
 237                        result = YAFFS_FAIL;
 238        }
 239
 240        if (st == dev->sum_tags && result == YAFFS_OK)
 241                bi->has_summary = 1;
 242
 243        return result;
 244}
 245
 246int yaffs_summary_add(struct yaffs_dev *dev,
 247                        struct yaffs_ext_tags *tags,
 248                        int chunk_in_nand)
 249{
 250        struct yaffs_packed_tags2_tags_only tags_only;
 251        struct yaffs_summary_tags *sum_tags;
 252        int block_in_nand = chunk_in_nand / dev->param.chunks_per_block;
 253        int chunk_in_block = chunk_in_nand % dev->param.chunks_per_block;
 254
 255        if (!dev->sum_tags)
 256                return YAFFS_OK;
 257
 258        if (chunk_in_block >= 0 && chunk_in_block < dev->chunks_per_summary) {
 259                yaffs_pack_tags2_tags_only(&tags_only, tags);
 260                sum_tags = &dev->sum_tags[chunk_in_block];
 261                sum_tags->chunk_id = tags_only.chunk_id;
 262                sum_tags->n_bytes = tags_only.n_bytes;
 263                sum_tags->obj_id = tags_only.obj_id;
 264
 265                if (chunk_in_block == dev->chunks_per_summary - 1) {
 266                        /* Time to write out the summary */
 267                        yaffs_summary_write(dev, block_in_nand);
 268                        yaffs_summary_clear(dev);
 269                        yaffs_skip_rest_of_block(dev);
 270                }
 271        }
 272        return YAFFS_OK;
 273}
 274
 275int yaffs_summary_fetch(struct yaffs_dev *dev,
 276                        struct yaffs_ext_tags *tags,
 277                        int chunk_in_block)
 278{
 279        struct yaffs_packed_tags2_tags_only tags_only;
 280        struct yaffs_summary_tags *sum_tags;
 281        if (chunk_in_block >= 0 && chunk_in_block < dev->chunks_per_summary) {
 282                sum_tags = &dev->sum_tags[chunk_in_block];
 283                tags_only.chunk_id = sum_tags->chunk_id;
 284                tags_only.n_bytes = sum_tags->n_bytes;
 285                tags_only.obj_id = sum_tags->obj_id;
 286                yaffs_unpack_tags2_tags_only(tags, &tags_only);
 287                return YAFFS_OK;
 288        }
 289        return YAFFS_FAIL;
 290}
 291
 292void yaffs_summary_gc(struct yaffs_dev *dev, int blk)
 293{
 294        struct yaffs_block_info *bi = yaffs_get_block_info(dev, blk);
 295        int i;
 296
 297        if (!bi->has_summary)
 298                return;
 299
 300        for (i = dev->chunks_per_summary;
 301             i < dev->param.chunks_per_block;
 302             i++) {
 303                if (yaffs_check_chunk_bit(dev, blk, i)) {
 304                        yaffs_clear_chunk_bit(dev, blk, i);
 305                        bi->pages_in_use--;
 306                        dev->n_free_chunks++;
 307                }
 308        }
 309}
 310