📄 reclaim.c
字号:
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.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.repli || i == -fs.ojournal.repli) {
fs.ojournal.repli = (fs.ojournal.repli < 0 ? -result : result);
tw(tr(TR_FUNC, TrDReclaim,
"Updated ojournal: repli %d -> %d \n",
i, fs.ojournal.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) {
is_reclaim_running = 0; // NOTEME: change to goto?
return EFFS_CORRUPTED;
}
}
BLOCK_RECLAIM_TEST(BLOCK_RECLAIM_NO_CLEAN, "Oops before clean old data block");
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);
BLOCK_RECLAIM_TEST(BLOCK_RECLAIM_CLEANING, "Oops before free old data block");
// Free the old block
block_free(b);
}
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, OT_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, OT_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. Note: if ffs
// is conf. with 1MB or above will all not used inodes default have
// the location to FFFF which will trigger a clean and make a error!
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));
// Testing of fs.testflags is for the sake of testing block_commit()
if ((fs.testflags & BLOCK_COMMIT_BASE) != 0) {
tw(tr(TR_FUNC, TrBlock, "Bailing out because fs.testflags = 0x%X\n",
fs.testflags));
}
else {
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++;
}
}
}
// 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));
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;
}
// Testing of fs.testflags is for the sake of testing block_commit()
if ((fs.testflags & BLOCK_COMMIT_BASE) != 0 &&
fs.testflags != BLOCK_COMMIT_OLD_FREE) {
tw(tr(TR_FUNC, TrBlock, "Bailing out because fs.testflags = 0x%X\n",
fs.testflags));
}
else {
// We must read block's age before we erase it.
bhp = (struct block_header_s *) offset2addr(dev.binfo[b].offset);
age = bhp->age;
ffsdrv.erase(b);
block_preformat(b, age);
}
tw(tr(TR_END, TrBlock, "} %d\n", 0));
return 0;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -