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#include <dm/devres.h>
  32
  33/*
  34 * The summary is built up in an array of summary tags.
  35 * This gets written to the last one or two (maybe more) chunks in a block.
  36 * A summary header is written as the first part of each chunk of summary data.
  37 * The summary header must match or the summary is rejected.
  38 */
  39
  40/* Summary tags don't need the sequence number because that is redundant. */
  41struct yaffs_summary_tags {
  42        unsigned obj_id;
  43        unsigned chunk_id;
  44        unsigned n_bytes;
  45};
  46
  47/* Summary header */
  48struct yaffs_summary_header {
  49        unsigned version;       /* Must match current version */
  50        unsigned block;         /* Must be this block */
  51        unsigned seq;           /* Must be this sequence number */
  52        unsigned sum;           /* Just add up all the bytes in the tags */
  53};
  54
  55
  56static void yaffs_summary_clear(struct yaffs_dev *dev)
  57{
  58        if (!dev->sum_tags)
  59                return;
  60        memset(dev->sum_tags, 0, dev->chunks_per_summary *
  61                sizeof(struct yaffs_summary_tags));
  62}
  63
  64
  65void yaffs_summary_deinit(struct yaffs_dev *dev)
  66{
  67        kfree(dev->sum_tags);
  68        dev->sum_tags = NULL;
  69        kfree(dev->gc_sum_tags);
  70        dev->gc_sum_tags = NULL;
  71        dev->chunks_per_summary = 0;
  72}
  73
  74int yaffs_summary_init(struct yaffs_dev *dev)
  75{
  76        int sum_bytes;
  77        int chunks_used; /* Number of chunks used by summary */
  78        int sum_tags_bytes;
  79
  80        sum_bytes = dev->param.chunks_per_block *
  81                        sizeof(struct yaffs_summary_tags);
  82
  83        chunks_used = (sum_bytes + dev->data_bytes_per_chunk - 1)/
  84                        (dev->data_bytes_per_chunk -
  85                                sizeof(struct yaffs_summary_header));
  86
  87        dev->chunks_per_summary = dev->param.chunks_per_block - chunks_used;
  88        sum_tags_bytes = sizeof(struct yaffs_summary_tags) *
  89                                dev->chunks_per_summary;
  90        dev->sum_tags = kmalloc(sum_tags_bytes, GFP_NOFS);
  91        dev->gc_sum_tags = kmalloc(sum_tags_bytes, GFP_NOFS);
  92        if (!dev->sum_tags || !dev->gc_sum_tags) {
  93                yaffs_summary_deinit(dev);
  94                return YAFFS_FAIL;
  95        }
  96
  97        yaffs_summary_clear(dev);
  98
  99        return YAFFS_OK;
 100}
 101
 102static unsigned yaffs_summary_sum(struct yaffs_dev *dev)
 103{
 104        u8 *sum_buffer = (u8 *)dev->sum_tags;
 105        int i;
 106        unsigned sum = 0;
 107
 108        i = sizeof(struct yaffs_summary_tags) *
 109                                dev->chunks_per_summary;
 110        while (i > 0) {
 111                sum += *sum_buffer;
 112                sum_buffer++;
 113                i--;
 114        }
 115
 116        return sum;
 117}
 118
 119static int yaffs_summary_write(struct yaffs_dev *dev, int blk)
 120{
 121        struct yaffs_ext_tags tags;
 122        u8 *buffer;
 123        u8 *sum_buffer = (u8 *)dev->sum_tags;
 124        int n_bytes;
 125        int chunk_in_nand;
 126        int chunk_in_block;
 127        int result;
 128        int this_tx;
 129        struct yaffs_summary_header hdr;
 130        int sum_bytes_per_chunk = dev->data_bytes_per_chunk - sizeof(hdr);
 131        struct yaffs_block_info *bi = yaffs_get_block_info(dev, blk);
 132
 133        buffer = yaffs_get_temp_buffer(dev);
 134        n_bytes = sizeof(struct yaffs_summary_tags) *
 135                                dev->chunks_per_summary;
 136        memset(&tags, 0, sizeof(struct yaffs_ext_tags));
 137        tags.obj_id = YAFFS_OBJECTID_SUMMARY;
 138        tags.chunk_id = 1;
 139        chunk_in_block = dev->chunks_per_summary;
 140        chunk_in_nand = dev->alloc_block * dev->param.chunks_per_block +
 141                                                dev->chunks_per_summary;
 142        hdr.version = YAFFS_SUMMARY_VERSION;
 143        hdr.block = blk;
 144        hdr.seq = bi->seq_number;
 145        hdr.sum = yaffs_summary_sum(dev);
 146
 147        do {
 148                this_tx = n_bytes;
 149                if (this_tx > sum_bytes_per_chunk)
 150                        this_tx = sum_bytes_per_chunk;
 151                memcpy(buffer, &hdr, sizeof(hdr));
 152                memcpy(buffer + sizeof(hdr), sum_buffer, this_tx);
 153                tags.n_bytes = this_tx + sizeof(hdr);
 154                result = yaffs_wr_chunk_tags_nand(dev, chunk_in_nand,
 155                                                buffer, &tags);
 156
 157                if (result != YAFFS_OK)
 158                        break;
 159                yaffs_set_chunk_bit(dev, blk, chunk_in_block);
 160                bi->pages_in_use++;
 161                dev->n_free_chunks--;
 162
 163                n_bytes -= this_tx;
 164                sum_buffer += this_tx;
 165                chunk_in_nand++;
 166                chunk_in_block++;
 167                tags.chunk_id++;
 168        } while (result == YAFFS_OK && n_bytes > 0);
 169        yaffs_release_temp_buffer(dev, buffer);
 170
 171
 172        if (result == YAFFS_OK)
 173                bi->has_summary = 1;
 174
 175
 176        return result;
 177}
 178
 179int yaffs_summary_read(struct yaffs_dev *dev,
 180                        struct yaffs_summary_tags *st,
 181                        int blk)
 182{
 183        struct yaffs_ext_tags tags;
 184        u8 *buffer;
 185        u8 *sum_buffer = (u8 *)st;
 186        int n_bytes;
 187        int chunk_id;
 188        int chunk_in_nand;
 189        int chunk_in_block;
 190        int result;
 191        int this_tx;
 192        struct yaffs_summary_header hdr;
 193        struct yaffs_block_info *bi = yaffs_get_block_info(dev, blk);
 194        int sum_bytes_per_chunk = dev->data_bytes_per_chunk - sizeof(hdr);
 195
 196        buffer = yaffs_get_temp_buffer(dev);
 197        n_bytes = sizeof(struct yaffs_summary_tags) * dev->chunks_per_summary;
 198        chunk_in_block = dev->chunks_per_summary;
 199        chunk_in_nand = blk * dev->param.chunks_per_block +
 200                                                        dev->chunks_per_summary;
 201        chunk_id = 1;
 202        do {
 203                this_tx = n_bytes;
 204                if (this_tx > sum_bytes_per_chunk)
 205                        this_tx = sum_bytes_per_chunk;
 206                result = yaffs_rd_chunk_tags_nand(dev, chunk_in_nand,
 207                                                buffer, &tags);
 208
 209                if (tags.chunk_id != chunk_id ||
 210                        tags.obj_id != YAFFS_OBJECTID_SUMMARY ||
 211                        tags.chunk_used == 0 ||
 212                        tags.ecc_result > YAFFS_ECC_RESULT_FIXED ||
 213                        tags.n_bytes != (this_tx + sizeof(hdr)))
 214                                result = YAFFS_FAIL;
 215                if (result != YAFFS_OK)
 216                        break;
 217
 218                if (st == dev->sum_tags) {
 219                        /* If we're scanning then update the block info */
 220                        yaffs_set_chunk_bit(dev, blk, chunk_in_block);
 221                        bi->pages_in_use++;
 222                }
 223                memcpy(&hdr, buffer, sizeof(hdr));
 224                memcpy(sum_buffer, buffer + sizeof(hdr), this_tx);
 225                n_bytes -= this_tx;
 226                sum_buffer += this_tx;
 227                chunk_in_nand++;
 228                chunk_in_block++;
 229                chunk_id++;
 230        } while (result == YAFFS_OK && n_bytes > 0);
 231        yaffs_release_temp_buffer(dev, buffer);
 232
 233        if (result == YAFFS_OK) {
 234                /* Verify header */
 235                if (hdr.version != YAFFS_SUMMARY_VERSION ||
 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
 247int 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
 276int 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
 293void 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