www.pudn.com > efs.rar > fs_ftl_cache.c


/****************************************************************************** 
 * fs_ftl_cache.c 
 * 
 * This file implements the cache Layer for the FTL to improve performance of 
 *   FS + FTL layer. 
 * 
 * Copyright (C) 2006 Qualcomm, Inc.  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_ftl_cache.c#8 $ 
  $DateTime: 2006/11/29 17:32:19 $ 
  $Author: davidb $ 
 
when         who     what, where, why 
--------     ---     ---------------------------------------------------------- 
2006-11-27   yg      Aligned buffers to 32 byte for interaction with DM. 
2006-11-22   yg      Corrected feature comment. 
2006-10-11   yg      Added counters support. 
2006-10-10   yg      Don't force periodic cache flush. 
2006-09-28   yg      Changed age & its handling to uint32 range. 
2006-09-20   dlb     Lint cleanup. 
2006-09-14   sh      Remove unused Benchmark hook. 
2006-08-31   dlb     Fix RCS header. 
2006-04-10   yg      Initial file for FTL Cache layer 
=============================================================================*/ 
 
#include "comdef.h" 
#include "customer.h" 
#include "err.h" 
 
#include  
 
#ifdef FEATURE_EFS_FTL 
 
#include "fs_ftl_cache.h" 
#include "fs_ftl_cache_i.h" 
#include "rex.h" 
#include "fs_compat.h" 
 
#include "fs_ftl_device.h" 
 
/*   Convert Sector number to block number based on the number of 
 *   512 byte pages that a physical block can accomodate. 
 */ 
#define SECTOR_TO_PHY_BLK(x)  ((x) / ftl_dev_sectors_per_block) 
 
/* 
 *    Cached block: holders for the cached pages with block number tag. 
 */ 
static block_cache_type   cached_block[MAX_CACHE_BLOCKS]; 
 
/* 
 *     Cached pages pool: the actual cache pages. Align it so that if the 
 *     access to these buffers involves DM, then 32 byte misaligned buffers 
 *     do not work or work slow because of unnecessary memcpy. 
 */ 
__align(32) static cached_page_type   cache_pages_pool[FTL_CACHE_PAGE_COUNT]; 
 
/* 
 *    Free pool, which can be used to instantaneously access the free page 
 */ 
static cached_page_type *free_cache_pool; 
 
/* 
 *   Do we have the poll timer active? while writing we can make sure we start 
 *   the poll timer and after syncing everything, we can turn off the timer. 
 */ 
static uint8 sync_timer_active; 
 
/* 
 *   Number of sectors (512 byte) in a physical block of flash device 
 */ 
static uint32 ftl_dev_sectors_per_block; 
 
/* 
 *    Maximum number of sectors available in FTL device externally 
 */ 
static uint32 ftl_dev_sector_count; 
 
/* 
 *    Array of buffer pointers to pass to FTL write function. 
 */ 
cached_buffer_list_type cached_ftl_buffers[MAX_FTL_BUFFER_LIST]; 
 
/* 
 *   Running age to track the oldest block available in the Cache 
 */ 
uint32 oldest_block_age; 
 
/*  This can be used to monitor the number of writes happened since last 
 *  poll. If we want to flush if no activity this can be used */ 
static uint32 write_count_since_last_sync_poll = 0; 
 
#ifdef FS_UNIT_TEST 
#error code not present 
#else 
/* 
 *   Since sync will be done in a different task, so we need to handle 
 *    synchronization between them. 
 */ 
rex_crit_sect_type fs_ftl_cache_crit_sec; 
 
/* Critical section related macros for Lock and release */ 
#  define FTL_CACHE_INIT_LOCK() rex_init_crit_sect (&fs_ftl_cache_crit_sec) 
#  define FTL_CACHE_GLOBAL_LOCK()  rex_enter_crit_sect (&fs_ftl_cache_crit_sec) 
#  define FTL_CACHE_RELEASE_LOCK() rex_leave_crit_sect (&fs_ftl_cache_crit_sec) 
#endif 
 
/****************************************************************************** 
FUNCTION      ftl_cache_init 
 
DESCRIPTION   Initialize the FTL Cache layer. 
 
PARAMETERS    None 
 
RETURN VALUE  0 
******************************************************************************/ 
int 
ftl_cache_init (void) 
{ 
  uint32 index, blk_cnt, pg_per_blk; 
  cached_page_type *previous_cache_page; 
  uint16 pg_sz; 
 
  /* Initialize lower FTL layer */ 
  if (ftl_raw_init() != FTL_SUCCESS) 
  { 
    return FTL_FAILURE; 
  } 
 
  /* Get FTL device level constants for cache usage configuration */ 
  if (ftl_get_raw_dev_params (&blk_cnt, &pg_per_blk, 
                              &pg_sz, &ftl_dev_sector_count) == FTL_FAILURE) 
  { 
    return FTL_FAILURE; 
  } 
 
  /* Initialize critical section */ 
  FTL_CACHE_INIT_LOCK(); 
 
  /* calculate how many sectors are needed to cache 1 physical flash block */ 
  ftl_dev_sectors_per_block = pg_per_blk * (pg_sz / DEFAULT_MS_PAGE_SIZE); 
 
  /* Initialize our cached pages and place them in free pool */ 
  previous_cache_page = INVALID_CACHE_PAGE; 
  for (index = 0; index < FTL_CACHE_PAGE_COUNT; ++index) 
  { 
    cache_pages_pool[index].dirty        = 0; 
    cache_pages_pool[index].sector_num   = INVALID_PAGE; 
    cache_pages_pool[index].next         = previous_cache_page; 
 
    previous_cache_page = &cache_pages_pool[index]; 
  } 
  free_cache_pool = previous_cache_page; 
 
  /* Age will start now */ 
  oldest_block_age = 0; 
 
  /* Initialize the block holders */ 
  for (index = 0; index < MAX_CACHE_BLOCKS; ++index) 
  { 
    cached_block[index].dirty         = 0; 
    cached_block[index].sector_count  = 0; 
    cached_block[index].age           = 0; 
    cached_block[index].block_number  = INVALID_BLOCK; 
    cached_block[index].cached_pages  = INVALID_CACHE_PAGE; 
  } 
 
  /* Initialize our polling and sync routine variables */ 
  write_count_since_last_sync_poll = 0; 
  sync_timer_active = 0; 
 
  return FTL_SUCCESS; 
} 
 
/****************************************************************************** 
FUNCTION      ftl_cache_read 
 
DESCRIPTION   Read specified number of sectors from mentioned sector onwards. 
              Get the contents from either cache or directly from flash 
              depending on the availability. 
 
PARAMETERS 
          dn        : Currently not used by the FTL layer. 
          sector    : Starting Sector number to read from. 
          buffer    : buffer where read data has to be placed. 
          count     : Number of sectors to read starting from mentioned sector. 
 
RETURN VALUE 
              0 : Success 
     Error code : Failure 
******************************************************************************/ 
int 
ftl_cache_read (uint16 dn, uint32 sector, byte *buffer, uint16 count) 
{ 
  cached_page_type *temp_cached_page; 
 
  /* First do some sanity check */ 
  if ((sector >= ftl_dev_sector_count) 
      || ((sector + count) > ftl_dev_sector_count)) 
  { 
    return FTL_FAILURE; 
  } 
 
  ftl_device_read_counter (sector, count, FTL_OP_CACHE); 
 
  /* Enter critical section */ 
  FTL_CACHE_GLOBAL_LOCK(); 
 
  /*   First let's read everything from FTL assuming we don't have 
   *   anything in cache. 
   */ 
  ftl_raw_read(dn, sector, buffer, count); 
 
  /*   Now go on looking for the sectors one at a time until we searched for 
   *   all and overwrite if we have latest copy of the sector in cache 
   */ 
  while (count > 0) 
  { 
    /* Check if we can find this particular sector in cache */ 
    temp_cached_page = lookup_sector_in_cache(sector); 
 
    /* If we found the cache page, then copy it to the buffer */ 
    if (CACHED_PAGE_IS_VALID(temp_cached_page)) 
    { 
      memcpy (buffer, temp_cached_page->buffer, DEFAULT_MS_PAGE_SIZE); 
    } 
 
    /* update for next sector search */ 
    buffer += DEFAULT_MS_PAGE_SIZE; 
    --count; 
    ++sector; 
  } 
 
  /* Release lock */ 
  FTL_CACHE_RELEASE_LOCK(); 
 
  return FTL_SUCCESS; 
} 
 
/****************************************************************************** 
FUNCTION      ftl_cache_write 
 
DESCRIPTION   Cached Write multiple logical sectors starting from 
              the specified one. 
 
PARAMETERS 
          dn      : Currently not used by the FTL layer. 
          sector  : Starting Sector number to write to. 
          buffer  : buffer where new data to be written is available. 
          count   : Number of sectors to write starting from mentioned sector. 
 
RETURN VALUE  Success / Failure 
******************************************************************************/ 
int 
ftl_cache_write (uint16 dn, uint32 sector, byte *buffer, uint16 count) 
{ 
  cached_page_type *temp_cached_page; 
  block_cache_type *temp_cached_block; 
  uint32 sector_blk, got_new_page; 
 
  (void) dn; 
 
  /* First do some sanity check */ 
  if ((sector >= ftl_dev_sector_count) 
      || ((sector + count) > ftl_dev_sector_count)) 
  { 
    return FTL_FAILURE; 
  } 
 
  ftl_device_write_counter (sector, count, FTL_OP_CACHE); 
 
  /* Enter critical section */ 
  FTL_CACHE_GLOBAL_LOCK(); 
 
  if (sync_timer_active == 0) 
  { 
    fs_start_ftl_sync_timer (); 
    sync_timer_active = 1; 
  } 
 
  ++write_count_since_last_sync_poll; 
 
  sector_blk = SECTOR_TO_PHY_BLK (sector); 
  temp_cached_block = INVALID_BLOCK_CACHE; 
  temp_cached_page  = INVALID_CACHE_PAGE; 
 
  while (count > 0) 
  { 
    got_new_page = 0; 
 
    /* Check if we have this already cached */ 
    temp_cached_page = lookup_sector_in_cache (sector); 
    if (CACHED_PAGE_IS_NOT_VALID (temp_cached_page)) 
    { 
      /* So its not cached so far allocate a new page */ 
      temp_cached_page = get_free_cache_page (); 
 
      if (CACHED_PAGE_IS_NOT_VALID (temp_cached_page)) 
      { 
        /* Do something to sync and free up the cache */ 
        ERR_FATAL("Ran out of cache pages...??%d, %d, %d\n", 0, 0, 0); 
      } 
 
      /* With this we can add this page into cache */ 
      got_new_page = 1; 
 
      /* Update its contents to represent this sector */ 
      temp_cached_page->sector_num = sector; 
      temp_cached_page->dirty = 1; 
    } 
 
    /* Copy new data into the cached sector buffer */ 
    memcpy (temp_cached_page->buffer, buffer, DEFAULT_MS_PAGE_SIZE); 
 
    /* If we allocated new page then add it into the cache */ 
    if (got_new_page != 0) 
    { 
      add_page_to_cache (temp_cached_page); 
    } 
 
    /* Update to process the next sector */ 
    ++sector; 
    --count; 
    buffer += DEFAULT_MS_PAGE_SIZE; 
  } 
 
  /* Release lock */ 
  FTL_CACHE_RELEASE_LOCK(); 
 
  return FTL_SUCCESS; 
} 
 
/****************************************************************************** 
FUNCTION      ftl_cache_periodic_sync 
 
DESCRIPTION   Do periodic sync. This call will sync one oldest cached block 
              and return if there are more blocks to sync. 
 
PARAMETERS    None 
 
RETURN VALUE 
              0 : If no more blocks are cached. 
              1 : If there are more blocks in cache need to be flushed. 
******************************************************************************/ 
int 
ftl_cache_periodic_sync (void) 
{ 
  uint32 remainging_blocks_to_flush; 
 
  FTL_CACHE_GLOBAL_LOCK(); 
 
  if (write_count_since_last_sync_poll == 0) 
  { 
    remainging_blocks_to_flush = cache_sync_oldest_block (); 
 
    if (remainging_blocks_to_flush == 0) 
    { 
      sync_timer_active = 0; 
    } 
  } 
  else 
  { 
    /* We had write after our last poll, so let's just reset this 
     * and continue polling */ 
    write_count_since_last_sync_poll = 0; 
 
    /* returning back telling we have some blocks to flush, so 
     * that the timer is started again to check and sync later */ 
    remainging_blocks_to_flush = 1; 
  } 
 
  FTL_CACHE_RELEASE_LOCK(); 
 
  if (remainging_blocks_to_flush > 0) 
    return 1; 
  else 
    return 0; 
} 
 
/****************************************************************************** 
FUNCTION      ftl_cache_force_sync_all 
 
DESCRIPTION 
            Sync all the cache contents to flash. This would be done before 
            power down. 
 
PARAMETERS 
          None 
 
RETURN VALUE 
          0 : Success 
******************************************************************************/ 
int ftl_cache_force_sync_all (void) 
{ 
  /* Sync until we have no more un sync'd cached blocks in memory  */ 
 
  FTL_CACHE_GLOBAL_LOCK(); 
 
  while (cache_sync_oldest_block ()) 
    ; 
 
  sync_timer_active = 0; 
 
  FTL_CACHE_RELEASE_LOCK(); 
 
  return 0; 
} 
 
/****************************************************************************** 
FUNCTION      ftl_cache_get_size 
 
DESCRIPTION   Gets the geometry of the FTL device. 
 
PARAMETERS 
          drive_num : Currently not used by the FTL layer. 
          pblk_cnt  : Pointer to location to store the maximum available number 
                        of Sectors in this device (# of pages in flash term). 
          ppg_size  : Pointer to location to store the page size. 
 
RETURN VALUE  0 
******************************************************************************/ 
int 
ftl_cache_get_size (uint16 drive_num, uint32 *pblk_cnt, uint16 *ppg_size) 
{ 
  (void) drive_num; 
  if (pblk_cnt) 
  { 
    *pblk_cnt = ftl_dev_sector_count; 
  } 
 
  if (ppg_size) 
  { 
    *ppg_size = DEFAULT_MS_PAGE_SIZE; 
  } 
  return FTL_SUCCESS; 
} 
 
/*============================================================================= 
* 
*              L o c a l    f u n c t i o n s 
* 
=============================================================================*/ 
 
 
/****************************************************************************** 
FUNCTION      compare_less_than 
 
DESCRIPTION   Compare and check if the first parameter is less than second 
              parameter after taking care of wrap around of integer. 
              This function is customized for "BYTE ONLY", to make it 
              compatible with other data type, change the parameter type 
              and the range values to the respective first and last 
              quarter range of the data type chosen. 
 
PARAMETERS 
          x, y    : uint8 values to compare with wrap around taken 
                    into consideration. 
 
RETURN VALUE 
          Boolean value if x is less than y with wrap around taken care of. 
******************************************************************************/ 
 
#define FIRST_QUARTER_END     0x40000000 
#define LAST_QUARTER_START    0xC0000000 
 
static boolean 
compare_less_than (uint32 x, uint32 y) 
{ 
  if (((x < FIRST_QUARTER_END) && (y >= LAST_QUARTER_START)) 
      || ((y < FIRST_QUARTER_END) && (x >= LAST_QUARTER_START))) 
  { 
    return y < x; 
  } 
 
  return x < y; 
} 
 
/****************************************************************************** 
FUNCTION      cache_sync_oldest_block 
 
DESCRIPTION   Sync the oldest block around in the cache. 
 
PARAMETERS    None 
 
RETURN VALUE  Remaining blocks to flush in the cache. 
******************************************************************************/ 
static uint32 
cache_sync_oldest_block (void) 
{ 
  uint32 index, dirty_block_count = 0; 
  block_cache_type *oldest_block_in_cache = INVALID_BLOCK_CACHE; 
 
  for (index = 0; index < MAX_CACHE_BLOCKS; ++index) 
  { 
    if ((cached_block[index].block_number != INVALID_BLOCK) 
        && (cached_block[index].sector_count > 0) 
        && (cached_block[index].dirty != 0) 
        && CACHED_PAGE_IS_VALID (cached_block[index].cached_pages)) 
    { 
      ++dirty_block_count; 
 
      if (BLOCK_CACHE_IS_NOT_VALID (oldest_block_in_cache) 
          || (compare_less_than (cached_block[index].age, 
                                 oldest_block_in_cache->age))) 
      { 
        oldest_block_in_cache = &cached_block[index]; 
      } 
    } 
  } 
 
  if (BLOCK_CACHE_IS_VALID (oldest_block_in_cache)) 
  { 
    flush_block_cache (oldest_block_in_cache); 
    --dirty_block_count; 
  } 
 
  return dirty_block_count; 
} 
 
/****************************************************************************** 
FUNCTION      lookup_sector_in_cache 
 
DESCRIPTION   Lookup if the sector in question is available in block cache. 
 
PARAMETERS 
          sector    : Sector number. 
 
RETURN VALUE 
          Cached Page         : Success (sector available in the cache) 
          INVALID_CACHE_PAGE  : Failure 
******************************************************************************/ 
static cached_page_type* 
lookup_sector_in_cache (uint32 sector) 
{ 
  uint32 sector_blk; 
  cached_page_type *temp_cached_page = INVALID_CACHE_PAGE; 
  block_cache_type *temp_cached_block; 
 
  /* Check if the block for this sector is cached */ 
  sector_blk = SECTOR_TO_PHY_BLK (sector); 
  temp_cached_block = lookup_block_in_cache (sector_blk); 
 
  /* Found block in the cache..?? */ 
  if (BLOCK_CACHE_IS_VALID (temp_cached_block)) 
  { 
    temp_cached_page = 
      lookup_sector_in_cache_list (sector, temp_cached_block->cached_pages); 
  } 
 
  return temp_cached_page; 
} 
 
/****************************************************************************** 
FUNCTION      lookup_sector_in_cache_list 
 
DESCRIPTION   Lookup if the sector in question is available in cache list. 
 
PARAMETERS 
          sector    : Sector number. 
          cache_list: list to search in for the sector in question. 
 
RETURN VALUE 
          Cached Page         : Success (sector available in the list) 
          INVALID_CACHE_PAGE  : Failure 
******************************************************************************/ 
static cached_page_type* 
lookup_sector_in_cache_list (uint32 sector, cached_page_type *cache_list) 
{ 
  cached_page_type *temp_cached_page = cache_list; 
 
  while (CACHED_PAGE_IS_VALID(temp_cached_page)) 
  { 
    /*   All our lists are sorted lists, so if we are 
     *   already out of range, then don't search further. 
     */ 
    if (temp_cached_page->sector_num > sector) 
    { 
      return INVALID_CACHE_PAGE; 
    } 
 
    /* If we found what we wanted, just return it */ 
    if (temp_cached_page->sector_num == sector) 
    { 
      return temp_cached_page; 
    } 
 
    /* Get the next one in link */ 
    temp_cached_page = temp_cached_page->next; 
  } 
 
  /* We coudn't find it */ 
  return INVALID_CACHE_PAGE; 
} 
 
/****************************************************************************** 
FUNCTION      get_free_cache_page 
 
DESCRIPTION 
          Lookup if there is any free cache page that can be used. 
          - If a free page is available, then return it. 
          - If a free page is not available, then flush oldest block 
          - After flushing the block, free page will be available, return that. 
 
PARAMETERS 
          None 
 
RETURN VALUE 
          Cached Page         : Success, got a free cache page 
          INVALID_CACHE_PAGE  : Failure 
******************************************************************************/ 
static cached_page_type* 
get_free_cache_page (void) 
{ 
  cached_page_type *temp_cached_page; 
 
  /* If our free pool doesn't have any page then we have to flush a block */ 
  if (CACHED_PAGE_IS_NOT_VALID (free_cache_pool)) 
  { 
    cache_sync_oldest_block (); 
  }   /* No free pages were found in free pool */ 
 
  temp_cached_page = free_cache_pool; 
 
  if (CACHED_PAGE_IS_VALID(temp_cached_page)) 
  { 
    free_cache_pool = temp_cached_page->next; 
 
    /* Clear off the link, we don't want to have any links */ 
    temp_cached_page->next = INVALID_CACHE_PAGE; 
  } 
  else 
  { 
    ERR_FATAL("No free pages even after a flush. %d%d%d\n", 0, 0, 0); 
  } 
 
  return temp_cached_page; 
} 
 
/****************************************************************************** 
FUNCTION      lookup_block_in_cache 
 
DESCRIPTION 
          Check if we have a block assigned in cache. 
 
PARAMETERS 
          block : block number to look for in the block cache. 
 
RETURN VALUE 
          Cached block         : Success, Block is availble in cache 
          INVALID_BLOCK_CACHE  : Failure, block is NOT available in cache 
******************************************************************************/ 
static block_cache_type* 
lookup_block_in_cache (uint32 block) 
{ 
  int index; 
  block_cache_type *temp_cached_block = INVALID_BLOCK_CACHE; 
 
  for (index = 0; index < MAX_CACHE_BLOCKS; ++index) 
  { 
    if (cached_block[index].block_number == block) 
    { 
      temp_cached_block = &cached_block[index]; 
      break; 
    } 
  } 
 
  return temp_cached_block; 
} 
 
/****************************************************************************** 
FUNCTION      insert_page_into_list 
 
DESCRIPTION 
          Insert a page into the destination page list. 
 
PARAMETERS 
          src_page       : Source page to insert. 
          dest_page_list : Destination page list to insert into. 
 
RETURN VALUE 
          Head of the destination list : It might have changed. 
******************************************************************************/ 
static cached_page_type* 
insert_page_into_list (cached_page_type *src_page, 
                       cached_page_type *dest_page_list) 
{ 
  cached_page_type *next_page, *prev_page; 
 
  if (CACHED_PAGE_IS_NOT_VALID(src_page)) 
  { 
    ERR_FATAL("Invalid list to merge.. !!?? %d %d %d\n", 0, 0, 0); 
  } 
 
  prev_page = INVALID_CACHE_PAGE; 
  next_page = dest_page_list; 
 
  while (CACHED_PAGE_IS_VALID(next_page) && 
         (next_page->sector_num < src_page->sector_num)) 
  { 
    prev_page = next_page; 
    next_page = next_page->next; 
  } 
 
  src_page->next = next_page; 
 
  if (CACHED_PAGE_IS_VALID(prev_page)) 
  { 
    prev_page->next = src_page; 
    return dest_page_list; 
  } 
  else 
  { 
    return src_page; 
  } 
} 
 
/****************************************************************************** 
FUNCTION      alloc_cached_block 
 
DESCRIPTION 
          Allocate a cache block if available. 
          If not available, flush a block (that is not being written now) then 
            allocate the flushed block. 
 
PARAMETERS 
          None 
 
RETURN VALUE 
          Cached block         : Success, got a free cached block 
          INVALID_BLOCK_CACHE  : Failure 
******************************************************************************/ 
static block_cache_type* 
alloc_cached_block (void) 
{ 
  int index; 
  block_cache_type *temp_cached_block = INVALID_BLOCK_CACHE, 
                   *blk_good_to_flush = INVALID_BLOCK_CACHE; 
 
  /* search for any NON dirty blocks with max number of sectors in it 
   * This search will make sure we re use any of the recently flushed 
   * full block, which is the normal case if we are getting huge volume 
   * of writes into contiguous blocks. 
   */ 
  for (index = 0; index < MAX_CACHE_BLOCKS; ++index) 
  { 
    if (cached_block[index].dirty == 0) 
    { 
      if ((cached_block[index].sector_count == 0) 
          && (cached_block[index].block_number == INVALID_BLOCK) 
          && (CACHED_PAGE_IS_NOT_VALID(cached_block[index].cached_pages))) 
      { 
        temp_cached_block = &cached_block[index]; 
        break; 
      } 
    } 
    /* If the block is dirty, then try to check if this is a good candidate 
     * to flush in the event of we not having any free block caches */ 
    else 
    { 
      if (BLOCK_CACHE_IS_NOT_VALID (blk_good_to_flush) || 
          compare_less_than (cached_block[index].age, blk_good_to_flush->age)) 
      { 
        blk_good_to_flush = &cached_block[index]; 
      } 
    } 
  } 
 
  /* Check if we got something from above search, then let's use it */ 
  if (BLOCK_CACHE_IS_VALID (temp_cached_block)) 
  { 
    return temp_cached_block; 
  } 
 
  /*  Okay, we are here means we didn't find any NON dirty blocks out in the 
   *  block cache. But we should have a block that is a good candidate for 
   *  flushing. 
   */ 
 
  if (BLOCK_CACHE_IS_NOT_VALID(blk_good_to_flush)) 
  { 
    ERR_FATAL("Didn't find any good candidate for flush?? %d%d%d\n", 0, 0, 0); 
  } 
 
  if (flush_block_cache (blk_good_to_flush) == 0) 
  { 
    return blk_good_to_flush; 
  } 
 
  return 0; 
} 
 
/****************************************************************************** 
FUNCTION      flush_block_cache 
 
DESCRIPTION 
          Flush the block cache passed in. If the pages in block cache are not 
          completely contiguous then multiple FTL write will happen. Since we 
          don't know if there are any free pages available to read and flush 
          full block. Instead of slow multiple FTL writes. 
 
          This routine will return the cache pages back into free pool 
          and marks the block as unused. 
 
PARAMETERS 
          blk_cache  : Block cache to flush to flash. 
 
RETURN VALUE 
          0   : Success 
          -1  : Failure 
******************************************************************************/ 
static int 
flush_block_cache (block_cache_type *blk_cache) 
{ 
  uint32 count, start_sector; 
  cached_page_type *temp_cached_page, *prev_page; 
 
  temp_cached_page = blk_cache->cached_pages; 
 
  /* Some sanity check */ 
  if (CACHED_PAGE_IS_NOT_VALID (temp_cached_page) 
      || (blk_cache->sector_count == 0) 
      || (blk_cache->dirty == 0)) 
  { 
    ERR_FATAL("Cannot flush a block with no dirty pages... %d %d %d", 0, 0, 0); 
  } 
 
  /*   Get all the pages available in the block into the buffer list in a way 
   *   that FTL can understand to flush in one pass. 
   */ 
  count = 0; 
  while (CACHED_PAGE_IS_VALID (temp_cached_page)) 
  { 
    /* Update the values into our array which is used to convey to FTL */ 
    cached_ftl_buffers[count].sector_num = temp_cached_page->sector_num; 
    cached_ftl_buffers[count].data_ptr = temp_cached_page->buffer; 
 
    temp_cached_page = temp_cached_page->next; 
    count++; 
  } 
 
  /* Another sanity check if we overflow */ 
  if (count >= MAX_FTL_BUFFER_LIST) 
  { 
    ERR_FATAL("Insufficient write buffer array... %d %d %d\n", count, 
                      MAX_FTL_BUFFER_LIST, 0); 
  } 
 
  /* Will be an indication of termination point */ 
  cached_ftl_buffers[count].data_ptr = 0; 
  cached_ftl_buffers[count].sector_num = INVALID_PAGE; 
 
  /* Another sanity check */ 
  if (count != blk_cache->sector_count) 
  { 
    ERR_FATAL("count doesn't match...!? %d %d %d\n", 0, 0, 0); 
  } 
 
  /* Just give info as first sector in this block. The pages will be sorted 
   * properly to their places with the information provided in the array 
   * which we just prepared */ 
  start_sector = blk_cache->block_number * ftl_dev_sectors_per_block; 
 
  /* Send it to FTL and let it handle writing */ 
  if (ftl_raw_write (0, start_sector, cached_ftl_buffers, count) 
      != FTL_SUCCESS) 
  { 
    ERR_FATAL("Write Failure... %d %d %d\n", 0, 0, 0); 
  } 
 
  /*   Now its cleanup time, just reinitialize all the pages that we just 
   *   flushed and just link the whole chain back into free cache pool 
   */ 
  temp_cached_page = blk_cache->cached_pages; 
  prev_page = INVALID_CACHE_PAGE; 
 
  /* Lets clear all the meta data in page buffers */ 
  while (CACHED_PAGE_IS_VALID(temp_cached_page)) 
  { 
    temp_cached_page->dirty = 0; 
    temp_cached_page->sector_num = INVALID_PAGE; 
    prev_page = temp_cached_page; 
    temp_cached_page = temp_cached_page->next; 
  } 
 
  /* According to above loop we will have the prev_page valid */ 
  if (CACHED_PAGE_IS_NOT_VALID (prev_page)){ 
    ERR_FATAL ("Problem in chain..??%d %d %d\n", 0, 0, 0); 
  } 
 
  /* Insert the free pages back into free pool */ 
  prev_page->next = free_cache_pool; 
  free_cache_pool = blk_cache->cached_pages; 
 
  /* Clear values in the cache block */ 
  blk_cache->cached_pages = INVALID_CACHE_PAGE; 
  blk_cache->block_number = INVALID_BLOCK; 
  blk_cache->age = 0; 
  blk_cache->sector_count = 0; 
  blk_cache->dirty = 0; 
 
  return 0; 
} 
 
/****************************************************************************** 
FUNCTION      add_page_to_cache 
 
DESCRIPTION 
          Add the newly written cached page into cache. This routine will 
          - Add the page into appropriate block cache if available. 
          - Otherwise allocates a new block cache if free block available. 
          - If free block is not found, then flushes a block cache to 
             make space for the new block data. 
 
PARAMETERS 
          new_cached_page  : New cached page to add to block cache. 
 
RETURN VALUE 
          0      : Success, added the page to cache. 
          -1     : invalid cache page. 
          Fatal  : Failure 
******************************************************************************/ 
static int 
add_page_to_cache (cached_page_type *new_cached_page) 
{ 
  uint32 sector_blk; 
  block_cache_type *blk_cache; 
 
  /* Sanity check */ 
  if (CACHED_PAGE_IS_NOT_VALID(new_cached_page)) 
  { 
    ERR_FATAL("Invalid cached page to insert %d %d %d\n", 0, 0, 0); 
  } 
 
  /* Check if we have this block already cached */ 
  sector_blk = SECTOR_TO_PHY_BLK (new_cached_page->sector_num); 
  blk_cache = lookup_block_in_cache (sector_blk); 
 
  if (BLOCK_CACHE_IS_NOT_VALID (blk_cache)) 
  { 
    /* Need to allocate new block for this page */ 
    blk_cache = alloc_cached_block (); 
    if (BLOCK_CACHE_IS_NOT_VALID (blk_cache)) 
    { 
      ERR_FATAL("Didn't get valid block cache...%d%d%d\n", 0, 0, 0); 
    } 
 
    /* Update this new block's parameters */ 
    blk_cache->block_number = sector_blk; 
    blk_cache->age = oldest_block_age++; 
  } 
 
  /* Insert this page list into the block */ 
  blk_cache->cached_pages = 
    insert_page_into_list (new_cached_page, blk_cache->cached_pages); 
 
  /* Update more params */ 
  blk_cache->dirty = 1; 
  blk_cache->sector_count++; 
 
  /* Check if the block is fully filled for an immediate flush */ 
  if (((new_cached_page->sector_num % ftl_dev_sectors_per_block) 
        == (ftl_dev_sectors_per_block - 1)) 
      && (blk_cache->sector_count == ftl_dev_sectors_per_block)) 
  { 
    /*  Flush if we have full block worth of data. 
     */ 
    flush_block_cache (blk_cache); 
  } 
 
  return 0; 
} 
 
#else 
extern int __dont_complain_about_empty_file; 
#endif /* FEATURE_EFS_FTL */