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; 
}