Handle malloc failure in checkpoint open.
[yaffs2.git] / yaffs_checkptrw.c
1 /*
2  * YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
3  *
4  * Copyright (C) 2002-2007 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 const char *yaffs_checkptrw_c_version =
15         "$Id: yaffs_checkptrw.c,v 1.19 2009-06-19 01:35:46 charles Exp $";
16
17
18 #include "yaffs_checkptrw.h"
19 #include "yaffs_getblockinfo.h"
20
21 static int yaffs_CheckpointSpaceOk(yaffs_Device *dev)
22 {
23         int blocksAvailable = dev->nErasedBlocks - dev->nReservedBlocks;
24
25         T(YAFFS_TRACE_CHECKPOINT,
26                 (TSTR("checkpt blocks available = %d" TENDSTR),
27                 blocksAvailable));
28
29         return (blocksAvailable <= 0) ? 0 : 1;
30 }
31
32
33 static int yaffs_CheckpointErase(yaffs_Device *dev)
34 {
35         int i;
36
37         if (!dev->eraseBlockInNAND)
38                 return 0;
39         T(YAFFS_TRACE_CHECKPOINT, (TSTR("checking blocks %d to %d"TENDSTR),
40                 dev->internalStartBlock, dev->internalEndBlock));
41
42         for (i = dev->internalStartBlock; i <= dev->internalEndBlock; i++) {
43                 yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev, i);
44                 if (bi->blockState == YAFFS_BLOCK_STATE_CHECKPOINT) {
45                         T(YAFFS_TRACE_CHECKPOINT, (TSTR("erasing checkpt block %d"TENDSTR), i));
46                         if (dev->eraseBlockInNAND(dev, i - dev->blockOffset /* realign */)) {
47                                 bi->blockState = YAFFS_BLOCK_STATE_EMPTY;
48                                 dev->nErasedBlocks++;
49                                 dev->nFreeChunks += dev->nChunksPerBlock;
50                         } else {
51                                 dev->markNANDBlockBad(dev, i);
52                                 bi->blockState = YAFFS_BLOCK_STATE_DEAD;
53                         }
54                 }
55         }
56
57         dev->blocksInCheckpoint = 0;
58
59         return 1;
60 }
61
62
63 static void yaffs_CheckpointFindNextErasedBlock(yaffs_Device *dev)
64 {
65         int  i;
66         int blocksAvailable = dev->nErasedBlocks - dev->nReservedBlocks;
67         T(YAFFS_TRACE_CHECKPOINT,
68                 (TSTR("allocating checkpt block: erased %d reserved %d avail %d next %d "TENDSTR),
69                 dev->nErasedBlocks, dev->nReservedBlocks, blocksAvailable, dev->checkpointNextBlock));
70
71         if (dev->checkpointNextBlock >= 0 &&
72                         dev->checkpointNextBlock <= dev->internalEndBlock &&
73                         blocksAvailable > 0) {
74
75                 for (i = dev->checkpointNextBlock; i <= dev->internalEndBlock; i++) {
76                         yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev, i);
77                         if (bi->blockState == YAFFS_BLOCK_STATE_EMPTY) {
78                                 dev->checkpointNextBlock = i + 1;
79                                 dev->checkpointCurrentBlock = i;
80                                 T(YAFFS_TRACE_CHECKPOINT, (TSTR("allocating checkpt block %d"TENDSTR), i));
81                                 return;
82                         }
83                 }
84         }
85         T(YAFFS_TRACE_CHECKPOINT, (TSTR("out of checkpt blocks"TENDSTR)));
86
87         dev->checkpointNextBlock = -1;
88         dev->checkpointCurrentBlock = -1;
89 }
90
91 static void yaffs_CheckpointFindNextCheckpointBlock(yaffs_Device *dev)
92 {
93         int  i;
94         yaffs_ExtendedTags tags;
95
96         T(YAFFS_TRACE_CHECKPOINT, (TSTR("find next checkpt block: start:  blocks %d next %d" TENDSTR),
97                 dev->blocksInCheckpoint, dev->checkpointNextBlock));
98
99         if (dev->blocksInCheckpoint < dev->checkpointMaxBlocks)
100                 for (i = dev->checkpointNextBlock; i <= dev->internalEndBlock; i++) {
101                         int chunk = i * dev->nChunksPerBlock;
102                         int realignedChunk = chunk - dev->chunkOffset;
103
104                         dev->readChunkWithTagsFromNAND(dev, realignedChunk,
105                                         NULL, &tags);
106                         T(YAFFS_TRACE_CHECKPOINT, (TSTR("find next checkpt block: search: block %d oid %d seq %d eccr %d" TENDSTR),
107                                 i, tags.objectId, tags.sequenceNumber, tags.eccResult));
108
109                         if (tags.sequenceNumber == YAFFS_SEQUENCE_CHECKPOINT_DATA) {
110                                 /* Right kind of block */
111                                 dev->checkpointNextBlock = tags.objectId;
112                                 dev->checkpointCurrentBlock = i;
113                                 dev->checkpointBlockList[dev->blocksInCheckpoint] = i;
114                                 dev->blocksInCheckpoint++;
115                                 T(YAFFS_TRACE_CHECKPOINT, (TSTR("found checkpt block %d"TENDSTR), i));
116                                 return;
117                         }
118                 }
119
120         T(YAFFS_TRACE_CHECKPOINT, (TSTR("found no more checkpt blocks"TENDSTR)));
121
122         dev->checkpointNextBlock = -1;
123         dev->checkpointCurrentBlock = -1;
124 }
125
126
127 int yaffs_CheckpointOpen(yaffs_Device *dev, int forWriting)
128 {
129
130         /* Got the functions we need? */
131         if (!dev->writeChunkWithTagsToNAND ||
132                         !dev->readChunkWithTagsFromNAND ||
133                         !dev->eraseBlockInNAND ||
134                         !dev->markNANDBlockBad)
135                 return 0;
136
137         if (forWriting && !yaffs_CheckpointSpaceOk(dev))
138                 return 0;
139
140         if (!dev->checkpointBuffer)
141                 dev->checkpointBuffer = YMALLOC_DMA(dev->totalBytesPerChunk);
142         if (!dev->checkpointBuffer)
143                 return 0;
144
145
146         dev->checkpointPageSequence = 0;
147
148         dev->checkpointOpenForWrite = forWriting;
149
150         dev->checkpointByteCount = 0;
151         dev->checkpointSum = 0;
152         dev->checkpointXor = 0;
153         dev->checkpointCurrentBlock = -1;
154         dev->checkpointCurrentChunk = -1;
155         dev->checkpointNextBlock = dev->internalStartBlock;
156
157         /* Erase all the blocks in the checkpoint area */
158         if (forWriting) {
159                 memset(dev->checkpointBuffer, 0, dev->nDataBytesPerChunk);
160                 dev->checkpointByteOffset = 0;
161                 return yaffs_CheckpointErase(dev);
162         } else {
163                 int i;
164                 /* Set to a value that will kick off a read */
165                 dev->checkpointByteOffset = dev->nDataBytesPerChunk;
166                 /* A checkpoint block list of 1 checkpoint block per 16 block is (hopefully)
167                  * going to be way more than we need */
168                 dev->blocksInCheckpoint = 0;
169                 dev->checkpointMaxBlocks = (dev->internalEndBlock - dev->internalStartBlock)/16 + 2;
170                 dev->checkpointBlockList = YMALLOC(sizeof(int) * dev->checkpointMaxBlocks);
171                 if(!dev->checkpointBlockList)
172                         return 0;
173
174                 for (i = 0; i < dev->checkpointMaxBlocks; i++)
175                         dev->checkpointBlockList[i] = -1;
176         }
177
178         return 1;
179 }
180
181 int yaffs_GetCheckpointSum(yaffs_Device *dev, __u32 *sum)
182 {
183         __u32 compositeSum;
184         compositeSum =  (dev->checkpointSum << 8) | (dev->checkpointXor & 0xFF);
185         *sum = compositeSum;
186         return 1;
187 }
188
189 static int yaffs_CheckpointFlushBuffer(yaffs_Device *dev)
190 {
191         int chunk;
192         int realignedChunk;
193
194         yaffs_ExtendedTags tags;
195
196         if (dev->checkpointCurrentBlock < 0) {
197                 yaffs_CheckpointFindNextErasedBlock(dev);
198                 dev->checkpointCurrentChunk = 0;
199         }
200
201         if (dev->checkpointCurrentBlock < 0)
202                 return 0;
203
204         tags.chunkDeleted = 0;
205         tags.objectId = dev->checkpointNextBlock; /* Hint to next place to look */
206         tags.chunkId = dev->checkpointPageSequence + 1;
207         tags.sequenceNumber =  YAFFS_SEQUENCE_CHECKPOINT_DATA;
208         tags.byteCount = dev->nDataBytesPerChunk;
209         if (dev->checkpointCurrentChunk == 0) {
210                 /* First chunk we write for the block? Set block state to
211                    checkpoint */
212                 yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev, dev->checkpointCurrentBlock);
213                 bi->blockState = YAFFS_BLOCK_STATE_CHECKPOINT;
214                 dev->blocksInCheckpoint++;
215         }
216
217         chunk = dev->checkpointCurrentBlock * dev->nChunksPerBlock + dev->checkpointCurrentChunk;
218
219
220         T(YAFFS_TRACE_CHECKPOINT, (TSTR("checkpoint wite buffer nand %d(%d:%d) objid %d chId %d" TENDSTR),
221                 chunk, dev->checkpointCurrentBlock, dev->checkpointCurrentChunk, tags.objectId, tags.chunkId));
222
223         realignedChunk = chunk - dev->chunkOffset;
224
225         dev->writeChunkWithTagsToNAND(dev, realignedChunk,
226                         dev->checkpointBuffer, &tags);
227         dev->checkpointByteOffset = 0;
228         dev->checkpointPageSequence++;
229         dev->checkpointCurrentChunk++;
230         if (dev->checkpointCurrentChunk >= dev->nChunksPerBlock) {
231                 dev->checkpointCurrentChunk = 0;
232                 dev->checkpointCurrentBlock = -1;
233         }
234         memset(dev->checkpointBuffer, 0, dev->nDataBytesPerChunk);
235
236         return 1;
237 }
238
239
240 int yaffs_CheckpointWrite(yaffs_Device *dev, const void *data, int nBytes)
241 {
242         int i = 0;
243         int ok = 1;
244
245
246         __u8 * dataBytes = (__u8 *)data;
247
248
249
250         if (!dev->checkpointBuffer)
251                 return 0;
252
253         if (!dev->checkpointOpenForWrite)
254                 return -1;
255
256         while (i < nBytes && ok) {
257                 dev->checkpointBuffer[dev->checkpointByteOffset] = *dataBytes;
258                 dev->checkpointSum += *dataBytes;
259                 dev->checkpointXor ^= *dataBytes;
260
261                 dev->checkpointByteOffset++;
262                 i++;
263                 dataBytes++;
264                 dev->checkpointByteCount++;
265
266
267                 if (dev->checkpointByteOffset < 0 ||
268                    dev->checkpointByteOffset >= dev->nDataBytesPerChunk)
269                         ok = yaffs_CheckpointFlushBuffer(dev);
270         }
271
272         return i;
273 }
274
275 int yaffs_CheckpointRead(yaffs_Device *dev, void *data, int nBytes)
276 {
277         int i = 0;
278         int ok = 1;
279         yaffs_ExtendedTags tags;
280
281
282         int chunk;
283         int realignedChunk;
284
285         __u8 *dataBytes = (__u8 *)data;
286
287         if (!dev->checkpointBuffer)
288                 return 0;
289
290         if (dev->checkpointOpenForWrite)
291                 return -1;
292
293         while (i < nBytes && ok) {
294
295
296                 if (dev->checkpointByteOffset < 0 ||
297                         dev->checkpointByteOffset >= dev->nDataBytesPerChunk) {
298
299                         if (dev->checkpointCurrentBlock < 0) {
300                                 yaffs_CheckpointFindNextCheckpointBlock(dev);
301                                 dev->checkpointCurrentChunk = 0;
302                         }
303
304                         if (dev->checkpointCurrentBlock < 0)
305                                 ok = 0;
306                         else {
307                                 chunk = dev->checkpointCurrentBlock *
308                                         dev->nChunksPerBlock +
309                                         dev->checkpointCurrentChunk;
310
311                                 realignedChunk = chunk - dev->chunkOffset;
312
313                                 /* read in the next chunk */
314                                 /* printf("read checkpoint page %d\n",dev->checkpointPage); */
315                                 dev->readChunkWithTagsFromNAND(dev,
316                                                 realignedChunk,
317                                                 dev->checkpointBuffer,
318                                                 &tags);
319
320                                 if (tags.chunkId != (dev->checkpointPageSequence + 1) ||
321                                         tags.eccResult > YAFFS_ECC_RESULT_FIXED ||
322                                         tags.sequenceNumber != YAFFS_SEQUENCE_CHECKPOINT_DATA)
323                                         ok = 0;
324
325                                 dev->checkpointByteOffset = 0;
326                                 dev->checkpointPageSequence++;
327                                 dev->checkpointCurrentChunk++;
328
329                                 if (dev->checkpointCurrentChunk >= dev->nChunksPerBlock)
330                                         dev->checkpointCurrentBlock = -1;
331                         }
332                 }
333
334                 if (ok) {
335                         *dataBytes = dev->checkpointBuffer[dev->checkpointByteOffset];
336                         dev->checkpointSum += *dataBytes;
337                         dev->checkpointXor ^= *dataBytes;
338                         dev->checkpointByteOffset++;
339                         i++;
340                         dataBytes++;
341                         dev->checkpointByteCount++;
342                 }
343         }
344
345         return  i;
346 }
347
348 int yaffs_CheckpointClose(yaffs_Device *dev)
349 {
350
351         if (dev->checkpointOpenForWrite) {
352                 if (dev->checkpointByteOffset != 0)
353                         yaffs_CheckpointFlushBuffer(dev);
354         } else {
355                 int i;
356                 for (i = 0; i < dev->blocksInCheckpoint && dev->checkpointBlockList[i] >= 0; i++) {
357                         yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev, dev->checkpointBlockList[i]);
358                         if (bi->blockState == YAFFS_BLOCK_STATE_EMPTY)
359                                 bi->blockState = YAFFS_BLOCK_STATE_CHECKPOINT;
360                         else {
361                                 /* Todo this looks odd... */
362                         }
363                 }
364                 YFREE(dev->checkpointBlockList);
365                 dev->checkpointBlockList = NULL;
366         }
367
368         dev->nFreeChunks -= dev->blocksInCheckpoint * dev->nChunksPerBlock;
369         dev->nErasedBlocks -= dev->blocksInCheckpoint;
370
371
372         T(YAFFS_TRACE_CHECKPOINT, (TSTR("checkpoint byte count %d" TENDSTR),
373                         dev->checkpointByteCount));
374
375         if (dev->checkpointBuffer) {
376                 /* free the buffer */
377                 YFREE(dev->checkpointBuffer);
378                 dev->checkpointBuffer = NULL;
379                 return 1;
380         } else
381                 return 0;
382 }
383
384 int yaffs_CheckpointInvalidateStream(yaffs_Device *dev)
385 {
386         /* Erase the first checksum block */
387
388         T(YAFFS_TRACE_CHECKPOINT, (TSTR("checkpoint invalidate"TENDSTR)));
389
390         if (!yaffs_CheckpointSpaceOk(dev))
391                 return 0;
392
393         return yaffs_CheckpointErase(dev);
394 }
395
396
397