04ef96b09e84712634ffbc28ecd6a08d7acc491a
[yaffs2.git] / yaffs_summary.c
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 all the tags for the chunks in a block into packed tags
15  * (just the tags part - no ECC) in 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
23 #include "yaffs_summary.h"
24 #include "yaffs_packedtags2.h"
25 #include "yaffs_nand.h"
26
27 /* Summary tags don't need the sequence number becase that is redundant. */
28 struct yaffs_summary_tags {
29         unsigned obj_id;
30         unsigned chunk_id;
31         unsigned n_bytes;
32 };
33
34 static void yaffs_summary_clear(struct yaffs_dev *dev)
35 {
36         if(!dev->sum_tags)
37                 return;
38         memset(dev->sum_tags, 0, dev->chunks_per_summary *
39                 sizeof(struct yaffs_summary_tags));
40 }
41
42 int yaffs_summary_init(struct yaffs_dev *dev)
43 {
44         struct yaffs_summary_tags *sum;
45         int sum_bytes;
46         int chunks_used; /* Number of chunks used by summary */
47
48         sum_bytes = dev->param.chunks_per_block *
49                         sizeof(struct yaffs_summary_tags);
50
51         chunks_used = (sum_bytes + dev->data_bytes_per_chunk - 1)/
52                         dev->data_bytes_per_chunk;
53         dev->chunks_per_summary = dev->param.chunks_per_block - chunks_used;
54         sum = kmalloc(sizeof(struct yaffs_summary_tags) *
55                                 dev->chunks_per_summary, GFP_NOFS);
56         if(!sum)
57                 return YAFFS_FAIL;
58
59         dev->sum_tags = sum;
60         yaffs_summary_clear(dev);
61
62         return YAFFS_OK;
63 }
64
65 void yaffs_summary_deinit(struct yaffs_dev *dev)
66 {
67         if(dev->sum_tags) {
68                 kfree(dev->sum_tags);
69                 dev->sum_tags = NULL;
70         }
71         dev->chunks_per_summary = 0;
72 }
73
74 static int yaffs_summary_write(struct yaffs_dev *dev)
75 {
76         struct yaffs_ext_tags tags;
77         u8 *buffer;
78         u8 *sum_buffer = (u8 *)dev->sum_tags;
79         int n_bytes;
80         int chunk_in_nand;
81         int result;
82         int this_tx;
83
84         buffer = yaffs_get_temp_buffer(dev);
85         n_bytes = sizeof(struct yaffs_summary_tags) * dev->chunks_per_summary;
86         memset(&tags, 0, sizeof(struct yaffs_ext_tags));
87         tags.obj_id = YAFFS_OBJECTID_SUMMARY;
88         tags.chunk_id = 1;
89         chunk_in_nand = dev->alloc_block * dev->param.chunks_per_block +
90                                                 dev-> chunks_per_summary;
91         do {
92                 this_tx = n_bytes;
93                 if (this_tx > dev->data_bytes_per_chunk)
94                         this_tx = dev->data_bytes_per_chunk;
95                 memcpy(buffer, sum_buffer, this_tx);
96                 tags.n_bytes = this_tx;
97                 result = yaffs_wr_chunk_tags_nand(dev, chunk_in_nand,
98                                                 buffer, &tags);
99                 n_bytes -= this_tx;
100                 sum_buffer += this_tx;
101                 chunk_in_nand++;
102                 tags.chunk_id++;
103         } while (result == YAFFS_OK && n_bytes > 0);
104         yaffs_release_temp_buffer(dev, buffer);
105         return result;
106 }
107
108 int yaffs_summary_read(struct yaffs_dev *dev, int blk)
109 {
110         struct yaffs_ext_tags tags;
111         u8 *buffer;
112         u8 *sum_buffer = (u8 *)dev->sum_tags;
113         int n_bytes;
114         int chunk_id;
115         int chunk_in_nand;
116         int result;
117         int this_tx;
118
119         buffer = yaffs_get_temp_buffer(dev);
120         n_bytes = sizeof(struct yaffs_summary_tags) * dev->chunks_per_summary;
121         chunk_in_nand = blk * dev->param.chunks_per_block +
122                                                         dev->chunks_per_summary;
123         chunk_id = 1;
124         do {
125                 this_tx = n_bytes;
126                 if(this_tx > dev->data_bytes_per_chunk)
127                         this_tx = dev->data_bytes_per_chunk;
128                 result = yaffs_rd_chunk_tags_nand(dev, chunk_in_nand,
129                                                 buffer, &tags);
130
131                 if (tags.chunk_id != chunk_id ||
132                         tags.obj_id != YAFFS_OBJECTID_SUMMARY ||
133                         tags.chunk_used == 0 ||
134                         tags.ecc_result > YAFFS_ECC_RESULT_FIXED ||
135                         this_tx != tags.n_bytes)
136                                 result = YAFFS_FAIL;
137                 if (result != YAFFS_OK)
138                         break;
139
140                 memcpy(sum_buffer, buffer, this_tx);
141                 n_bytes -= this_tx;
142                 sum_buffer += this_tx;
143                 chunk_in_nand++;
144                 chunk_id++;
145                 dev->n_free_chunks--;
146         } while (result == YAFFS_OK && n_bytes > 0);
147         yaffs_release_temp_buffer(dev, buffer);
148         return result;
149 }
150 int yaffs_summary_add(struct yaffs_dev *dev,
151                         struct yaffs_ext_tags *tags,
152                         int chunk_in_nand)
153 {
154         struct yaffs_packed_tags2_tags_only tags_only;
155         struct yaffs_summary_tags *sum_tags;
156         int chunk_in_block = chunk_in_nand % dev->param.chunks_per_block;
157
158         if(!dev->sum_tags)
159                 return YAFFS_OK;
160
161         printf("Add summary for chunk %d tags (%d %d %d)",chunk_in_block,
162                         tags->seq_number, tags->obj_id, tags->chunk_id);
163         if(chunk_in_block >= 0 && chunk_in_block < dev->chunks_per_summary) {
164                 yaffs_pack_tags2_tags_only(&tags_only, tags);
165                 sum_tags = &dev->sum_tags[chunk_in_block];
166                 sum_tags->chunk_id = tags_only.chunk_id;
167                 sum_tags->n_bytes = tags_only.n_bytes;
168                 sum_tags->obj_id = tags_only.obj_id;
169                 printf(" added %d %d %d", sum_tags->obj_id, sum_tags->chunk_id, sum_tags->n_bytes);
170
171                 if(chunk_in_block == dev->chunks_per_summary - 1) {
172                         printf(" Write summary");
173                         /* Time to write out the summary */
174                         yaffs_summary_write(dev);
175                         yaffs_summary_clear(dev);
176                         yaffs_skip_rest_of_block(dev);
177                 }
178         }
179         printf("\n");
180         return YAFFS_OK;
181 }
182
183 int yaffs_summary_fetch(struct yaffs_dev *dev,
184                         struct yaffs_ext_tags *tags,
185                         int chunk_in_block)
186 {
187         struct yaffs_packed_tags2_tags_only tags_only;
188         struct yaffs_summary_tags *sum_tags;
189         if(chunk_in_block >= 0 && chunk_in_block < dev->chunks_per_summary) {
190                 sum_tags = &dev->sum_tags[chunk_in_block];
191                 tags_only.chunk_id = sum_tags->chunk_id;
192                 tags_only.n_bytes = sum_tags->n_bytes;
193                 tags_only.obj_id = sum_tags->obj_id;
194                 yaffs_unpack_tags2_tags_only(tags, &tags_only);
195                 return YAFFS_OK;
196         }
197         return YAFFS_FAIL;
198 }