www.pudn.com > efs.rar > fs_logr.c
/*===========================================================================
fs_logr.c - Log region handling.
Copyright (c) 2003 -- 2006 by QUALCOMM Incorporated. All Rights Reserved.
EDIT HISTORY FOR MODULE
This section contains comments describing changes made to the module.
Notice that changes are listed in reverse chronological order.
$Header: //depot/asic/MSMSHARED/services/efs/MSM_EFS.01.02/fs_logr.c#5 $ $DateTime: 2006/09/18 15:07:36 $ $Author: davidb $
when who what, where, why
-------- --- ------------------------------------------------------
2006-09-11 dlb Use flash driver wrappers.
2005-12-09 sh Terminate search for next good block after looping.
2005-05-26 sd Compilation fixes for L4.
2005-01-04 dlb Update copyright line.
2004-12-30 dlb Remove excess infiltration of factory image code.
2004-10-15 dlb Update copyright line.
2004-10-07 dlb Whitespace cleanup.
2003-06-13 gr Added support for recovery from erase failures.
2003-03-07 dlb Added fs_logr_peek_alloc().
2003-02-28 dlb Created.
===========================================================================*/
#include
#ifdef FEATURE_IG_EFS_EXT_SERVER
#include "amssassert.h"
#else
#include "assert.h"
#endif
#include "fs_logr.h"
#include "fs_pm_ptable.h"
#include "fs_err.h"
#include "fs_device.h"
#ifdef FS_UNIT_TEST
#error code not present
#endif
/* Check if a block is marked as bad in the bad block map.
*/
#define BLOCK_IS_BAD(logr,bl) \
(((logr)->super->data.u.nand.logr_badmap & \
(1 << ((bl) - (logr)->block_base))) != 0)
/* Record that a block is bad.
*/
#define RECORD_BAD_BLOCK(logr,bl) \
(logr)->super->data.u.nand.logr_badmap |= 1 << ((bl) - (logr)->block_base);
/* Increment a block value with proper wrapping. */
#define ADVANCE_BLOCK(logr, bl) \
do { \
(bl)++; \
if ((bl) >= (logr)->stop) \
(bl) = (logr)->start; \
} while (0)
/* Increment a block value skipping bad blocks. */
#define ADVANCE_TO_NEXT_GOOD_BLOCK(logr, bl) \
do { \
block_id starting_block = bl; \
ADVANCE_BLOCK((logr), (bl)); \
while (BLOCK_IS_BAD((logr), (bl))) { \
if (bl == starting_block) { \
FS_ERR_FATAL ("Too many bad blocks starting at %d", bl, 0, 0); \
} \
ADVANCE_BLOCK((logr), (bl)); \
} \
} while (0)
/* XXX: Initial assumption that this is only for NAND flash. Some day we
* may want to use this on NOR as well.
*/
void
fs_logr_init (
fs_logr_t logr,
fs_super_t super,
int fresh_start)
{
block_id bl;
/* Only written to support nand. */
ASSERT (FS_ISNAND (super));
/* Assumes there are exactly 4 regions. */
ASSERT (super->data.u.nand.num_regions == 4);
/* There are 32 bits for the log region bad block map. */
ASSERT (super->data.u.nand.regions[3] - super->data.u.nand.regions[2]
<= 32);
/* This define is limits the number of blocks as well. */
ASSERT (super->data.u.nand.regions[3] - super->data.u.nand.regions[2]
<= FS_LOGR_MAX_LOG_BLOCKS);
logr->super = super;
logr->block_base = super->data.u.nand.regions[2];
logr->start = super->data.u.nand.regions[2];
logr->stop = super->data.u.nand.regions[3];
if (fresh_start) {
super->data.u.nand.logr_badmap = 0;
for (bl = super->data.u.nand.regions[2];
bl < super->data.u.nand.regions[3];
bl++)
{
if (fs_flash_bad_block_check (super->dev, bl)) {
RECORD_BAD_BLOCK (logr, bl);
#ifdef FS_UNIT_TEST
#error code not present
#endif
} else {
/* Nothing to do, it will get erased before usage. */
}
}
logr->write_block = logr->start;
logr->write_page = 0;
} else {
/* The log starts at where the superblock indicates. */
ASSERT (super->data.log_head != INVALID_PAGE_ID);
logr->write_block = super->data.log_head >> super->block_shift;
logr->write_page = super->data.log_head & (~super->block_mask);
while (!fs_flash_is_erased (
super->dev,
(logr->write_block << super->block_shift)
+ logr->write_page))
{
logr->write_page++;
if (logr->write_page == super->data.block_size) {
logr->write_page = 1; /* Skip the superblock. */
ADVANCE_TO_NEXT_GOOD_BLOCK (logr, logr->write_block);
}
if (((logr->write_block << super->block_shift)
+ logr->write_page) == super->data.log_head)
{
FS_ERR_FATAL ("No space found in log", 0, 0, 0);
}
}
}
}
page_id
fs_logr_alloc (fs_logr_t logr)
{
int result;
page_id new_page;
if (logr->write_page == 0) {
/* Skip over any bad blocks. */
while (1) {
if (BLOCK_IS_BAD (logr, logr->write_block)) {
ADVANCE_TO_NEXT_GOOD_BLOCK (logr, logr->write_block);
}
result = fs_flash_erase_block (logr->super->dev,
logr->write_block, FS_FOP_LOG);
if (result != FS_DEVICE_OK) {
RECORD_BAD_BLOCK (logr, logr->write_block);
fs_flash_mark_block_bad (logr->super->dev,
logr->write_block);
#ifdef FS_UNIT_TEST
#error code not present
#endif
} else {
break;
}
}
#ifdef FS_UNIT_TEST
#error code not present
#endif
}
new_page = (logr->write_block << logr->super->block_shift)
+ logr->write_page;
logr->write_page++;
if (logr->write_page >= logr->super->data.block_size) {
logr->write_page = 0;
ADVANCE_TO_NEXT_GOOD_BLOCK (logr, logr->write_block);
}
#ifdef FS_UNIT_TEST
#error code not present
#endif
return new_page;
}
/* Where do we think we are going to alloc? */
page_id
fs_logr_peek_alloc (fs_logr_t logr)
{
page_id page = logr->write_page;
if (page == 0)
page = 1;
return (logr->write_block << logr->super->block_shift)
+ page;
}
/* Copy out the stopping block and page, and update those values as we
* iterate. The value is used by the iteration routines to replay journal
* entries correctly. */
void
fs_logr_iterate (fs_logr_t logr, fs_log_t log,
void (*visit) (void *priv, fs_log_code_t code, uint32 *args),
void *priv)
{
block_id block, stop_block;
page_id page, stop_page;
int result;
fs_log_code_t code;
uint32 args[3];
int done;
block = logr->super->data.log_head >> logr->super->block_shift;
page = logr->super->data.log_head & (~logr->super->block_mask);
stop_block = logr->write_block;
stop_page = logr->write_page;
while (block != stop_block || page != stop_page) {
logr->write_block = block;
logr->write_page = page;
result = fs_log_iter_set_page (log,
(block << logr->super->block_shift) + page,
NULL);
if (result == FS_LOG_ITER_GOOD) {
done = 0;
while (!done) {
fs_log_iter_step (log, &code, args);
if (code == LOG_CODE_GROUP_END)
done = 1;
else {
visit (priv, code, args);
}
}
fs_log_add_written_page (log,
(block << logr->super->block_shift) + page);
} else {
/* Do nothing if either corrupt, or just erased. */
}
/* Advance to the next page. */
page++;
if (page == logr->super->data.block_size) {
page = 1; /* Skip the superblock. */
ADVANCE_TO_NEXT_GOOD_BLOCK (logr, block);
}
}
/* Put these back so the next run will work correctly. */
logr->write_block = stop_block;
logr->write_page = stop_page;
}