www.pudn.com > drivers.rar > reclaim.c


/****************************************************************************** 
 * Flash File System (ffs) 
 * Idea, design and coding by Mads Meisner-Jensen, mmj@ti.com 
 * 
 * FFS core reclaim functionality 
 * 
 * $Id: reclaim.c 1.4.1.28 Thu, 08 Jan 2004 15:05:23 +0100 tsj $ 
 * 
 ******************************************************************************/ 
 
#ifndef TARGET 
#include "ffs.cfg" 
#endif 
 
#include "ffs/ffs.h" 
#include "ffs/board/core.h" 
#include "ffs/board/drv.h" 
#include "ffs/board/ffstrace.h" 
 
#include   // rand() 
 
/****************************************************************************** 
 * Inodes Reclaim 
 ******************************************************************************/ 
 
void inodes_recurse(iref_t i) 
{ 
    iref_t pi; 
    struct inode_s *ip, *newip; 
 
    tw(tr(TR_BEGIN, TrReclaimLow, "inodes_recurse(%d) {\n", i)); 
 
    ip    = inode_addr(i); 
    newip = (struct inode_s *) offset2addr(dev.binfo[fs.newinodes].offset) + i; 
     
    // copy inode dir to new block, except child, sibling and copied 
    ffsdrv.write((uint32*) &newip->location, (uint32*) &ip->location, sizeof(location_t)); 
    ffsdrv.write_halfword((uint16*) &newip->size,     ip->size); 
    ffsdrv_write_byte    (&newip->flags,    ip->flags); 
    ffsdrv.write_halfword((uint16*) &newip->sequence, ip->sequence); 
    ffsdrv.write_halfword((uint16*) &newip->updates,  ip->updates); 
    bstat[fs.newinodes].used++; 
 
    // if no children of this dir, we have no more work to do 
    if (get_child(ip) == (iref_t) IREF_NULL) { 
        tw(tr(TR_END, TrReclaimLow, "}\n")); 
        return; 
    } 
 
    pi = -i; 
    i = get_child(ip); 
    ip = inode_addr(i); 
 
    do { 
        tw(tr(TR_FUNC, TrReclaimLow, "pi = %d, i = %d", pi, i)); 
 
        tw(tr(TR_NULL, TrReclaimLow, ", size = %d, location = 0x%x", ip->size,  
              ip->location)); 
         
        tw(tr(TR_NULL, TrReclaimLow, ", name_addr = 0x%x", 
              addr2name(offset2addr(location2offset(ip->location))))); 
         
        if (is_object(ip, OTE_SEGMENT))  
            tw(tr(TR_NULL, TrReclaimLow, ", (segment)\n")); 
         
        else 
            tw(tr(TR_NULL, TrReclaimLow, ", '%s'\n", 
                  (ip->size ? addr2name(offset2addr(location2offset(ip->location))) 
                   : "(cleaned)"))); 
 
        if (is_object_valid(ip)) 
        { 
            if (is_object(ip, OTE_DIR)) { 
                tw(tr(TR_NULL, TrReclaimLow, "recursing...\n", i)); 
                inodes_recurse(i); 
            } 
            else { 
                tw(tr(TR_NULL, TrReclaimLow, "copying...\n")); 
                // copy inode to new block, except child, sibling and copied 
                newip = (struct inode_s *) 
                    offset2addr(dev.binfo[fs.newinodes].offset) + i; 
                ffsdrv.write((uint32*) &newip->location, (uint32*) &ip->location, sizeof(location_t)); 
                ffsdrv.write_halfword((uint16*) &newip->size,     ip->size); 
                ffsdrv_write_byte    (&newip->flags,    ip->flags); 
                ffsdrv.write_halfword((uint16*) &newip->sequence, ip->sequence); 
                ffsdrv.write_halfword((uint16*) &newip->updates,  ip->updates); 
                bstat[fs.newinodes].used++; 
            } 
 
            tw(tr(TR_FUNC, TrReclaimLow, "Linking: %d->%d\n",pi, i)); 
            // now write the child or sibling link of previous inode 
            newip = (struct inode_s *) 
                offset2addr(dev.binfo[fs.newinodes].offset); 
            if (pi > 0) 
                ffsdrv.write_halfword((uint16*) &(newip + pi)->sibling, i); 
            else 
                ffsdrv.write_halfword((uint16*) &(newip + (-pi))->child, i); 
             
            pi = i; // save index of previous inode 
             
            if (get_child(ip) != (iref_t) IREF_NULL && is_object(ip, OTE_FILE)) { 
                iref_t pis, is; 
                struct inode_s *ips; 
                pis = i; 
                ips = ip; 
 
                tw(tr(TR_FUNC, TrReclaimLow, "Follow segment head\n")); 
                // While child is valid 
                while ((is = get_child(ips)) != (iref_t) IREF_NULL) { 
 
                    // Get child 
                    is = get_child(ips); 
                    ips = inode_addr(is); 
                    tw(tr(TR_FUNC, TrReclaimLow, "Child ok, got new child i = %d\n", is)); 
                    // While object not is valid 
                    while (!is_object_valid(ips)) { 
                        tw(tr(TR_FUNC, TrReclaimLow, "pi = %d, i = %d c(cleaned)\n", pis, is)); 
                        // If sibling are valid 
                        if (get_sibling(ips) != (iref_t) IREF_NULL) {   
                            // Get sibling 
                            is = get_sibling(ips); 
                            ips = inode_addr(is); 
                            tw(tr(TR_FUNC, TrReclaimLow, "Sibling ok, got new sibling i = %d\n", is)); 
                        } 
                        else { 
                            tw(tr(TR_FUNC, TrReclaimLow, "Sibling = FF (%d)\n", ips->sibling)); 
                            break;  // Nothing more todo, child and sibling = FF 
                        } 
                    } 
                    // If object is valid 
                    if (is_object_valid(ips)) { 
                        tw(tr(TR_NULL, TrReclaimLow, "copying...\n")); 
                        // copy inode to new block, except child, sibling and copied 
                        newip = (struct inode_s *) 
                            offset2addr(dev.binfo[fs.newinodes].offset) + is; 
                        ffsdrv.write((uint32*) &newip->location, (uint32*) &ips->location, sizeof(location_t)); 
                        ffsdrv.write_halfword((uint16*) &newip->size,     ips->size); 
                        ffsdrv_write_byte    (&newip->flags,              ips->flags); 
                        ffsdrv.write_halfword((uint16*) &newip->sequence, ips->sequence); 
                        ffsdrv.write_halfword((uint16*) &newip->updates,  ips->updates); 
                        bstat[fs.newinodes].used++; 
                         
                        tw(tr(TR_FUNC, TrReclaimLow, "Linking child: %d->%d\n",pis, is)); 
                        // now write the child link of previous inode 
                        newip = (struct inode_s *) 
                            offset2addr(dev.binfo[fs.newinodes].offset); 
                        ffsdrv.write_halfword((uint16*) &(newip + (pis))->child, is); 
                         
                        pis = is; // save index of previous inode    
                
                    }      
                    else { 
                        tw(tr(TR_FUNC, TrReclaimLow, "Sibling = FF (%d, %d)\n",  
                              ips->sibling, ips->child)); 
                    } 
 
                } 
            } 
        }        
        else { 
            tw(tr(TR_NULL, TrReclaimLow, "(ignoring)\n")); 
        } 
        i = get_sibling(ip); 
        ip = inode_addr(i); 
             
    } while (i != (iref_t) IREF_NULL); 
     
    tw(tr(TR_END, TrReclaimLow, "}\n")); 
} 
 
// Reclaim inodes, eg. move inodes to another block and erase old one. 
effs_t inodes_reclaim(void) 
{ 
    tw(tr(TR_BEGIN, TrIReclaim, "inodes_reclaim() {\n")); 
    ttw(str(TTrRec, "irec{")); 
 
    if (fs.initerror != EFFS_OK) { 
        tw(tr(TR_END, TrIReclaim, "} %d\n", fs.initerror)); 
        ttw(ttr(TTrRec, "} %d" NL, fs.initerror)); 
        return fs.initerror; 
    } 
 
    if ((fs.newinodes = block_alloc(1, BF_COPYING)) < 0) { 
        tw(tr(TR_END, TrIReclaim, "} %d\n", EFFS_NOBLOCKS)); 
        ttw(ttr(TTrRec, "} %d" NL, EFFS_NOBLOCKS)); 
        return EFFS_NOBLOCKS; 
    } 
 
    statistics_update_irec(bstat[fs.inodes].used - bstat[fs.inodes].lost,  
                           bstat[fs.inodes].lost); 
 
    // copy all inodes... 
    bstat[fs.newinodes].used = 0; 
    inodes_recurse(fs.root); 
 
    // The replacement inodes are not copied to the new block thus the table 
    // must be cleaned 
    memset(fs.repi_table, 0, sizeof(fs.repi_table)); 
 
    block_commit(); 
 
    tw(tr(TR_END, TrIReclaim, "} 0\n")); 
    ttw(str(TTrRec, "} 0" NL)); 
 
    return EFFS_OK; 
} 
 
// Inode -> Lost, Copying -> Inode, Lost -> Free 
void block_commit(void) 
{ 
    int oldinodes = fs.inodes; 
 
    tw(tr(TR_BEGIN, TrIReclaim, "block_commit(%d -> %d) {\n",  
       oldinodes, fs.newinodes)); 
    ttw(ttr(TTrRec, "block_commit(%d -> %d) {\n" NL,  
       oldinodes, fs.newinodes));  
 
    block_flags_write(oldinodes, BF_LOST); 
  
    // Validate new block as an inodes block 
    block_flags_write(fs.newinodes, BF_INODES); 
 
    bstat[fs.newinodes].lost = 0; 
    bstat[fs.newinodes].objects = 1; 
    inodes_set(fs.newinodes); 
 
    // Free old inodes block 
    block_free(oldinodes); 
 
    ttw(str(TTrRec, "} 0" NL)); 
    tw(tr(TR_END, TrIReclaim, "}\n")); 
} 
 
 
/****************************************************************************** 
 * Data Reclaim 
 ******************************************************************************/ 
 
// Important note: We must NOT perform a data reclaim when we are in the 
// process of creating the journal file! 
 
// Reclaim a data block, eg. move files to other blocks and erase old one. 
// When the reclaim is done, we must completely delete the old inodes which 
// are pointing into the old data sector which is going to be erased now. 
iref_t data_reclaim(void) 
{ 
    iref_t error; 
 
    tw(tr(TR_BEGIN, TrDReclaim, "data_reclaim() {\n")); 
 
    if (fs.initerror != EFFS_OK) { 
        tw(tr(TR_END, TrDReclaim, "} %d\n", fs.initerror)); 
        return fs.initerror; 
    } 
 
    error = data_reclaim_try(); 
 
    tw(tr(TR_END, TrDReclaim, "} (data_reclaim) %d\n", error)); 
 
    return error; 
} 
 
// This algorithm will reclaim a block when it exceeds a delta age of more 
// than DAGE_MAX and the age gain is more than DAGE_MAX. When the block is 
// younger than DAGE_MAX, the agegain needed (for accepting the reclaim) is 
// decreased linearly with the block's increase in delta age. 
// 
// The algorithm is extended a wee bit in order to avoid data reclaim 
// storms, i.e. when many static data blocks reach DAGE_MAX at the same 
// time. What we do is that we allow a few early age based reclaims this 
// way: The probability that a block is reclaimed early gets exponentially 
// higher as the block's delta age is closing in on DAGE_MAX - provided that 
// the agegain is good. 
int dage_max_reached(int dage_blk, int agegain) 
{ 
    int reclaim, early, log2, mask; 
 
    tw(tr(TR_BEGIN, TrDReclaim, "young(%d, %d) {\n", dage_blk, agegain)); 
     
    // Simple algorithm 
    reclaim = (dage_blk + agegain - 2 * FFS_DAGE_MAX >= 0); 
 
    // Early exponential probability based reclaim 
    early = FFS_DAGE_MAX - dage_blk; 
    if (agegain > dage_blk - 4 && 0 < early && early <= FFS_DAGE_EARLY_WIDTH) { 
        if (early < 4) 
            early = 2; 
        if (early < FFS_DAGE_EARLY_WIDTH) { 
            // Now make an exponential probability distributon by 
            // generating a bitmask of a size relative to (dage_blk 
            // - DAGE_EARLY_WIDTH) 
            log2 = -1; 
            while (early > 0) { 
                early >>= 1; 
                log2++; 
            } 
            reclaim = log2; 
             
            mask = (1 << (log2 + 1)) - 1; 
            reclaim = ((rand() & mask) == 0); 
        } 
    } 
 
    // Do not perform a reclaim unless we gain a certain minimum 
    if (agegain < FFS_DAGE_GAIN_MIN) 
        reclaim = 0; 
 
    tw(tr(TR_END, TrDReclaim, "} (%d)\n", reclaim)); 
    return reclaim; 
} 
 
 
// Find a suitable block to reclaim. On success, return the number of bytes 
// actually reclaimed. Otherwise, on failure, return a (negative) error. 
int data_reclaim_try(void) 
{ 
    int result = 0, reserved_ok = 0; 
    bref_t b, blocks_free; 
    bref_t brc_young_b, brc_lost_b; 
 
    blocksize_t brc_lost_lost; 
    blocksize_t unused, lost, lost_total, free; 
 
    age_t brc_young_dage, free_dage, dage; 
    struct block_header_s *bhp; 
    // Note gain can be negative if the free block is younger than the 
    // youngest data block 
    int age_gain;  
 
    tw(tr(TR_BEGIN, TrDReclaim, "data_reclaim_try() {\n")); 
    ttw(str(TTrRec, "drec{" NL)); 
    // While searching for a block to reclaim, we maintain two block reclaim 
    // candidates (brc):  
    // 
    // 1- The youngest block, e.g. the one with the largest age distance to 
    // fs.age_max. This is to ensure that blocks with a high amount of 
    // static data are used for wear-leveling. 
    // 
    // 2- The block with the highest amount of lost bytes. Note this 
    // candidate is also used if the amount of free space is below 
    // RESERVED_LOW. 
     
    // This counts free blocks, so we initialize to number of blocks minus 
    // one for inodes. 
    blocks_free = dev.numblocks - 1; 
 
    // Initialize Block Reclaim Candidate (brc) variables 
    brc_lost_b   = -1; brc_lost_lost   = 0; 
    brc_young_b  = -1; brc_young_dage = 0;  free_dage  = 0; 
 
    lost_total   = 0; 
 
    tw(tr(TR_FUNC, TrDReclaim, 
          "blk  unused    lost  w/age   age dist  objs\n")); 
    for (b = 0; b < dev.numblocks; b++) 
    { 
        bhp = (struct block_header_s *) offset2addr(dev.binfo[b].offset); 
 
        if (is_block(b, BF_IS_DATA)) 
        { 
            // Record number of lost bytes and number of unused bytes, 
            // eg. total space that would be freed if this block was 
            // reclaimed 
            lost   = bstat[b].lost; 
            unused = dev.blocksize - (bstat[b].used - bstat[b].lost); 
            free   = dev.blocksize - bstat[b].used; 
 
            lost_total   += lost; 
 
            if (free >= RESERVED_LOW)  
                reserved_ok = 1; 
            if (lost > brc_lost_lost) { 
                brc_lost_b = b; 
                brc_lost_lost = lost; 
            } 
            tw(tr(TR_FUNC, TrDReclaim, "%3d %7d %7d ", b, unused, lost)); 
 
            dage = saturate_dage(fs.age_max - bhp->age); 
 
            tw(tr(TR_NULL, TrDReclaim, "%6d %5d %4d   %3d\n", 
                  lost, bhp->age, dage, bstat[b].objects)); 
 
            if (dage >= brc_young_dage) { 
                brc_young_b = b; 
                brc_young_dage = dage; 
            } 
            blocks_free--; 
        } 
        else if (is_block(b, BF_IS_FREE)) { 
            // Find youngest free block (in must cases we will only have one free b) 
            dage = saturate_dage(fs.age_max - bhp->age); 
 
            if (dage >= free_dage) 
                free_dage = dage;   // Delta age of youngest free block 
        } 
    } 
    tw(tr(TR_FUNC, TrDReclaim, "sum %7d\n", lost_total)); 
    tw(tr(TR_FUNC, TrDReclaim, "blocks_free = %d, fs.age_max = %d\n", blocks_free, fs.age_max)); 
 
    age_gain = brc_young_dage - free_dage; // Same as free - block age 
 
    // If the amount of free space is below RESERVED_LOW we MUST not reclaim 
    // the youngest block because the youngest block does not necessarily 
    // contain any lost space that can be freed and in some cases we needs free 
    // space for a new journal file. 
     if (reserved_ok == 0) { 
        tw(tr(TR_FUNC, TrDReclaim,  
              "No reserved, reclaim most-lost block (%d)\n", brc_lost_b)); 
        result = data_block_reclaim(brc_lost_b, MOST_LOST); 
    } 
    else if (dage_max_reached(brc_young_dage, age_gain) > 0 ) { 
        tw(tr(TR_FUNC, TrDReclaim, "Reclaiming youngest block (%d)\n", 
              brc_young_b)); 
        result = data_block_reclaim(brc_young_b, YOUNGEST); 
    } 
    else { 
        tw(tr(TR_FUNC, TrDReclaim, "Reclaiming most-lost block (%d)\n", 
              brc_lost_b)); 
        result = data_block_reclaim(brc_lost_b, MOST_LOST); 
    } 
 
    tw(tr(TR_END, TrDReclaim, "} (data_reclaim_try) %d\n", result)); 
 
    return result; 
} 
 
iref_t data_block_reclaim(bref_t b, int candidate) 
{ 
    // 1. If there are more objects in this block than there are remaining 
    // free inodes, we have to make an inodes_reclaim() first. 
    // 
    // 2. Relocate each valid object from old block (to another block). An 
    // object relocation is similar to a normal file update, e.g. similar to 
    // fupdate(). 
    // 
    // 3. set BF_CLEANING flag of old block. 
    // 
    // 4. ALL inodes (also invalid an erased ones) referring into reclaimed 
    // block must now be totally wiped out. 
    // 
    // 5. Free (invalidate) old block. 
 
    iref_t i, n, j; 
    blocksize_t used_old, lost_old; 
	int org_res_space, result = 0; 
	iref_t org_block_files_reserved; 
    offset_t lower, upper; 
    struct inode_s *ip; 
 
    tw(tr(TR_BEGIN, TrDReclaim, "data_block_reclaim(%d) {\n", b)); 
 
    // In case of no free blocks (after sudden power off) or if the file 
    // system is near full we risk to be reentered (infinity recursively 
    // loop) and we can not allow that, so just return. 
	if (fs.is_reclaim_running == 1) { 
		tw(tr(TR_END, TrDReclaim, "} (reenteret skip reclaim) 0\n")); 
		return EFFS_RECLAIMLOOP; 
	} 
	fs.is_reclaim_running = 1; 
 
    // If there are more objects in this block than there are remaining 
    // free inodes, we have to make an inodes_reclaim() first. 
    tw(tr(TR_FUNC, TrDReclaim, 
          "block_objects, fs.inodes_max, inodes: used, free\n")); 
    tw(tr(TR_FUNC, TrDReclaim, 
          "%10d, %13d, %15d, %4d\n", 
          bstat[b].objects, 
          fs.inodes_max, bstat[fs.inodes].used, 
          fs.inodes_max - (bstat[fs.inodes].used + bstat[fs.inodes].lost))); 
 
    if (bstat[b].objects >= (fs.inodes_max - (bstat[fs.inodes].used +  
                                              bstat[fs.inodes].lost +  
                                              FFS_INODES_MARGIN))) { 
        tw(tr(TR_FUNC, TrInode, "NOTE: Will run out of free inodes...\n")); 
        inodes_reclaim(); 
    } 
 
    // Allocate a new block. NOTE: we don't return an error because if we 
	// get in the situation where we don't have any free blocks this is the 
	// only way to recover. 
    if ((block_alloc(1, BF_DATA)) < 0) { 
        tw(tr(TR_FUNC, TrAll, "WARNING: block_alloc failed\n")); 
    } 
 
    // If there are any objects at all to reclaim... 
    if (bstat[b].objects > 0) 
    { 
        // Save the current journal state 
        if (journal_push() != EFFS_OK) { 
			fs.is_reclaim_running = 0;       // NOTEME: change to goto? 
			return EFFS_CORRUPTED; 
		} 
 
        // We simulate that this block is completely full, such that we 
        // don't relocate files to the end of the block 
        used_old = bstat[b].used; 
        lost_old = bstat[b].lost;  // For statistics 
        bstat[b].used = dev.blocksize - 1; 
 
 
        // Compute lower (inclusive) and upper (exclusive) bounds of the 
        // location of files in this block 
        lower = offset2location(dev.binfo[b].offset); 
        upper = offset2location(dev.binfo[b].offset + dev.blocksize); 
 
        tw(tr(TR_FUNC, TrDReclaim, "Block addr range = 0x%X..0x%X\n", 
              location2offset(lower), location2offset(upper))); 
 
		// This is the only time we are allowed to use the reserved  
		org_block_files_reserved= fs.block_files_reserved; 
		fs.block_files_reserved = 0; 
 
		org_res_space = fs.reserved_space; 
        fs.reserved_space = RESERVED_NONE; 
 
		ip = inode_addr(1); 
        for (i = 1, n = 0; i < fs.inodes_max; i++, ip++) 
        { 
            // Ensure object is valid and within the block to be reclaimed 
            if (is_object_valid(ip) && 
                lower <= ip->location && ip->location < upper) 
            { 
                if ((result = object_relocate(i)) < 0) { 
                    tw(tr(TR_FUNC, TrAll, "FATAL object_relocate failed\n")); 
                    break; 
				} 
                 
                // If we reclaim a segment head or wch that is in use we must 
                // update the file descriptor as well 
                for (j = 0; j < fs.fd_max; j++) { 
                    if (i == fs.fd[j].seghead) { 
                        tw(tr(TR_FUNC, TrDReclaim,  
                              "Updated seghead %d -> %d \n", 
                              fs.fd[j].seghead, result)); 
                        fs.fd[j].seghead = result; 
                    } 
                    if (i == fs.fd[j].wch) { 
                        tw(tr(TR_FUNC, TrDReclaim,  
                              "Updated wch %d -> %d \n", 
                              fs.fd[j].wch, result)); 
                        fs.fd[j].wch = result; 
                    } 
                } 
 
                // If we have just reclaimed an object which we started on 
                // updating we must also update ojournal 
                if (i == fs.ojournal.oldi) { 
                    struct inode_s *ip = inode_addr(result); 
                    tw(tr(TR_FUNC, TrDReclaim,  
                          "Updated ojournal oldi %d -> %d \n", 
                          fs.ojournal.oldi, result)); 
                    fs.ojournal.oldi     = result; 
                    fs.ojournal_ram.location = ip->location; 
                } 
 
                if (i == fs.ojournal.diri || i == -fs.ojournal.diri) { 
                    fs.ojournal.diri = (fs.ojournal.diri < 0 ? -result : result); 
                    tw(tr(TR_FUNC, TrDReclaim,  
                          "Updated ojournal: diri %d -> %d \n",  
                          i, fs.ojournal.diri)); 
                } 
 
                if (i == fs.ojournal_ram.repli || i == -fs.ojournal_ram.repli) { 
                    fs.ojournal_ram.repli = (fs.ojournal_ram.repli < 0 ? -result : result); 
                    tw(tr(TR_FUNC, TrDReclaim,  
                          "Updated ojournal: repli %d -> %d \n",  
                          i, fs.ojournal_ram.repli)); 
                } 
  
                if (i == fs.i_backup || i == -fs.i_backup) { 
                    fs.i_backup = (fs.i_backup < 0 ? -result : result); 
                    tw(tr(TR_FUNC, TrDReclaim,  
                          "Updated i_backup: %d -> %d \n", i, fs.i_backup)); 
                } 
 
                n++; 
            } 
        } 
 
		fs.block_files_reserved = org_block_files_reserved; // Restore 
		fs.reserved_space = org_res_space; 
 
        tw(tr(TR_FUNC, TrDReclaim, "Reclaimed %d objects\n", n)); 
        if (result >= 0) 
            result = n; // We return number of objects relocated 
 
        if (i < fs.inodes_max) { 
            // We did not finish, so restore the old bstat[].used of the block. 
            bstat[b].used = used_old; 
            tw(tr(TR_FUNC, TrAll, 
                  "WARNING: data_block_reclaim() not completed\n")); 
            result = EFFS_DBR; 
		} 
 
        // Restore the saved journal state 
        if (journal_pop() != EFFS_OK) { 
			fs.is_reclaim_running = 0;       // NOTEME: change to goto? 
			return EFFS_CORRUPTED; 
		} 
    } 
 
    if (result >= 0) { 
        // Clean the block (remove all inodes that refer to this block) 
        block_flags_write(b, BF_CLEANING); 
        block_clean(b); 
 
        statistics_update_drec(used_old - lost_old, lost_old, candidate);  
     
        // Free the old block 
        block_free(b); 
    } 
 
	fs.is_reclaim_running = 0; 
 
    tw(tr(TR_END, TrDReclaim, "} (data_block_reclaim) %d\n", result)); 
    ttw(ttr(TTrRec, "} %d" NL, result)); 
 
    return result; 
} 
 
// Relocate object represented by inode reference .  
iref_t object_relocate(iref_t oldi) 
{ 
    iref_t newi; 
    struct inode_s *oldip; 
    char *olddata, *oldname; 
    int oldsize; 
 
    tw(tr(TR_BEGIN, TrReclaimLow, "object_relocate(%d) {\n", oldi)); 
 
    journal_begin(oldi); 
 
    oldip = inode_addr(oldi); 
 
    oldsize = segment_datasize(oldip); 
    olddata = offset2addr(location2offset(oldip->location)); 
    oldname = addr2name(olddata); 
    olddata = addr2data(olddata, oldip); 
     
    if (is_object(oldip, OTE_SEGMENT))      
        newi = segment_create(olddata, oldsize, -oldi);    
    else { 
        // root inode is a special case 
        if (*oldname == '/') 
            newi = object_create(oldname, olddata, oldsize, 0); 
        else  
            newi = object_create(oldname, olddata, oldsize, oldi); 
    } 
 
    if (newi < 0) { 
        tw(tr(TR_END, TrReclaimLow, "} %d\n", newi)); 
        return newi; 
    } 
 
    // root inode is a special case 
    if ((*oldname == '/') && !is_object(oldip, OTE_SEGMENT)) { 
        tw(tr(TR_FUNC, TrDReclaim, "Relocating fs.root: %d->%d\n", oldi, newi)); 
        fs.root = newi; 
    } 
 
    journal_end(0); 
 
    tw(tr(TR_END, TrReclaimLow, "} %d\n", newi)); 
 
    return newi; 
} 
 
// Clean a block, eg. erase all inodes that refer to this block. 
iref_t block_clean(bref_t b) 
{ 
    iref_t i, n; 
    struct inode_s *ip; 
    offset_t lower, upper; 
 
    tw(tr(TR_FUNC, TrDReclaim, "block_clean(%d) { ", b)); 
 
    // Compute lower (inclusive) and upper (exclusive) bounds of the 
    // location of files in this block 
    lower = offset2location(dev.binfo[b].offset); 
    upper = offset2location(dev.binfo[b].offset + dev.blocksize); 
 
    tw(tr(TR_FUNC, TrDReclaim, "offset range = 0x%X..0x%X: ", lower, upper)); 
 
    ip = inode_addr(1); 
    for (i = 1, n = 0; i < fs.inodes_max; i++, ip++) 
    { 
        // Ensure object is within the block to be reclaimed.  
        if (lower <= ip->location && upper > ip->location) 
        { 
            tw(tr(TR_NULL, TrReclaimLow, "%d ", i)); 
            // Set the size to zero so it won't be counted in ffs_initialize() 
            ffsdrv.write_halfword((uint16 *) &ip->size, 0); 
            n++; 
        } 
    } 
    tw(tr(TR_NULL, TrDReclaim, "} %d\n", n)); 
 
    return n; 
} 
 
 
/****************************************************************************** 
 * Main and block reclaim 
 ******************************************************************************/ 
 
// Reclaim (erase) all blocks that are marked as invalid/reclaimable. Each 
// time a block is erased, its age is incremented so as to support wear 
// levelling. Also, the global age limits are updated.  FIXME: Should we 
// avoid having ffs_initialize() do a block_reclaim() because it delays reboot?. 
int blocks_reclaim(void) 
{ 
    bref_t b, n, b_lost_space; 
	int blocks_free = 0, lost_space; 
 
	int free_space, b_free_space; 
 
    tw(tr(TR_BEGIN, TrBlock, "blocks_reclaim() {\n")); 
    ttw(str(TTrRec, "blocks_reclaim() {" NL)); 
 
    for (b = 0, n = 0; b < dev.numblocks; b++) { 
        if (is_block_flag(b, BF_LOST)) { 
            block_reclaim(b); 
            n++; 
        } 
        if (is_block(b, BF_IS_FREE)) { 
            blocks_free++; 
        } 
    } 
	ttr(TTrTask, "b=%d" NL, b); 
	// If the number of free blocks is less than fs.blocks_free_min we 
	// call data_block_reclaim(). We will reclaim the block with most lost 
	// space. This should only happend if we got a sudden power off/reset 
	// while we reclaimed a block. 
	if (blocks_free < fs.blocks_free_min) { 
		lost_space = 0; 
		free_space = 0; 
 
		// We most never reclaim the block with most free space because this 
		// is the only block we can relocate the objects to. 
		for (b = 0; b < dev.numblocks; b++) { 
			if (is_block_flag(b, BF_DATA)) { 
				if ((dev.blocksize - bstat[b].used) > free_space) { 
					free_space = dev.blocksize - bstat[b].used; 
					b_free_space = b; 
				} 
			} 
		} 
		tw(tr(TR_FUNC, TrBlock, "most free space: %d in block: %d \n",  
			  free_space, b_free_space)); 
 
		for (b = 0; b < dev.numblocks; b++) { 
			if (is_block_flag(b, BF_DATA) && b != b_free_space) { 
				if (bstat[b].lost > lost_space) { 
					lost_space = bstat[b].lost; 
					b_lost_space = b; 
				} 
			} 
		} 
		tw(tr(TR_FUNC, TrBlock, "most lost space: %d in block: %d \n",  
			  lost_space, b_lost_space)); 
 
		data_block_reclaim(b_lost_space, MOST_LOST); 
	} 
    tw(tr(TR_END, TrBlock, "} %d\n", n)); 
    ttw(ttr(TTrRec, "} %d" NL, n)); 
	ttr(TTrTask, "n = %d, free blocks=%d" NL, n, blocks_free); 
 
    return n; 
} 
 
int block_reclaim(bref_t b) 
{ 
    age_t age; 
    struct block_header_s *bhp; 
 
    tw(tr(TR_BEGIN, TrBlock, "block_reclaim(%d) {\n", b)); 
 
    // In ffs_initialize() we set fs.initerror = EFFS_INVALID while we call 
    // blocks_fsck(). We test for that condition now, in order to avoid 
    // doing sector erases that will delay the whole target boot process. 
    if (fs.initerror == EFFS_INVALID) { 
        tw(tr(TR_END, TrBlock, "} %d\n", fs.initerror)); 
        return fs.initerror; 
    } 
 
    // We must read block's age before we erase it. 
    bhp = (struct block_header_s *) offset2addr(dev.binfo[b].offset); 
    age = bhp->age; 
 
    POWERFAIL_DOMAIN_BEGIN(PFM_ERASE | PFM_NEXTERASE); 
    ffsdrv.erase(b); 
    POWERFAIL_DOMAIN_END(); 
 
    block_preformat(b, age); 
 
    tw(tr(TR_END, TrBlock, "} %d\n", 0)); 
 
    return 0; 
}