📄 reclaim.c
字号:
// 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 <i>.
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;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -