bfs: support moving the journal

Add a way to request allocation of a specific block run to
BlockAllocator. Use it to allocate the new location for the journal.

Change-Id: I276de77d937253baa312049ad2964849c5e9b68b
Reviewed-on: https://review.haiku-os.org/c/haiku/+/10057
Tested-by: Commit checker robot <[email protected]>
Reviewed-by: Adrien Destugues <[email protected]>
This commit is contained in:
ahenriksson
2026-01-22 16:53:25 +00:00
committed by Axel Dörfler
parent eba66a0e71
commit 2f76872b8e
4 changed files with 160 additions and 7 deletions
@@ -1,5 +1,5 @@
/*
* Copyright 2001-2020, Axel Dörfler, [email protected].
* Copyright 2001-2025, Axel Dörfler, [email protected].
* This file may be used under the terms of the MIT License.
*/
@@ -1112,6 +1112,46 @@ BlockAllocator::Allocate(Transaction& transaction, Inode* inode,
}
/*! Attempts to allocate a specific block run.
*/
status_t
BlockAllocator::AllocateBlockRun(Transaction& transaction, block_run run)
{
RecursiveLocker lock(fLock);
if (run.AllocationGroup() >= fNumGroups)
return B_BAD_VALUE;
if (!IsCompletelyInsideAllowedRange(run))
return B_DEVICE_FULL;
uint32 bitsPerBlock = fVolume->BlockSize() << 3;
AllocationGroup& group = fGroups[run.AllocationGroup()];
AllocationBlock cached(fVolume);
int32 end = run.Start() + run.Length();
// check that the requested blocks are free
for (int32 block = run.Start(); block < end; block++) {
if (cached.SetTo(group, block / bitsPerBlock) < B_OK)
RETURN_ERROR(B_ERROR);
if (cached.IsUsed(block % bitsPerBlock))
return B_DEVICE_FULL;
}
status_t status = group.Allocate(transaction, run.Start(), run.Length());
if (status != B_OK)
return status;
fVolume->SuperBlock().used_blocks
= HOST_ENDIAN_TO_BFS_INT64(fVolume->UsedBlocks() + run.Length());
return B_OK;
}
status_t
BlockAllocator::Free(Transaction& transaction, block_run run)
{
@@ -41,11 +41,14 @@ public:
status_t Allocate(Transaction& transaction, Inode* inode,
off_t numBlocks, block_run& run,
uint16 minimum = 1);
status_t Free(Transaction& transaction, block_run run);
status_t AllocateBlocks(Transaction& transaction,
int32 group, uint16 start, uint16 numBlocks,
uint16 minimum, block_run& run);
status_t AllocateBlockRun(Transaction& transaction,
block_run run);
status_t Free(Transaction& transaction, block_run run);
status_t Trim(uint64 offset, uint64 size,
uint64& trimmedSize);
+111 -4
View File
@@ -1,5 +1,5 @@
/*
* Copyright 2001-2020, Axel Dörfler, axeld@pinc-software.de.
* Copyright 2001-2025, Axel Dörfler, axeld@pinc-software.de.
* This file may be used under the terms of the MIT License.
*/
@@ -924,17 +924,19 @@ Journal::_WriteTransactionToLog()
/*! Flushes the current log entry to disk. If \a flushBlocks is \c true it will
also write back all dirty blocks for this volume.
also write back all dirty blocks for this volume. If \a movingLog is \c
true, we allow the lock to be held when the function is called.
*/
status_t
Journal::_FlushLog(bool canWait, bool flushBlocks)
Journal::_FlushLog(bool canWait, bool flushBlocks, bool movingLog)
{
status_t status = canWait ? recursive_lock_lock(&fLock)
: recursive_lock_trylock(&fLock);
if (status != B_OK)
return status;
if (recursive_lock_get_recursion(&fLock) > 1) {
int32 allowedLocks = movingLog ? 2 : 1;
if (recursive_lock_get_recursion(&fLock) > allowedLocks) {
// whoa, FlushLogAndBlocks() was called from inside a transaction
recursive_lock_unlock(&fLock);
return B_OK;
@@ -1097,6 +1099,111 @@ Journal::_TransactionDone(bool success)
}
status_t
Journal::MoveLog(block_run newLog)
{
block_run oldLog = fVolume->Log();
if (newLog == oldLog)
return B_OK;
off_t newEnd = newLog.Start() + newLog.Length();
off_t oldEnd = oldLog.Start() + oldLog.Length();
// make sure the new log position is ok
if (newLog.AllocationGroup() != 0)
return B_BAD_VALUE;
if (fVolume->ValidateBlockRun(newLog) != B_OK)
return B_BAD_VALUE;
if (newLog.Start() < 1 + fVolume->NumBitmapBlocks())
return B_BAD_VALUE;
if (newEnd > fVolume->NumBlocks())
return B_BAD_VALUE;
status_t status;
block_run allocatedRun = {};
BlockAllocator& allocator = fVolume->Allocator();
// allocate blocks if necessary
if (newEnd > oldEnd) {
if (oldEnd > newLog.Start())
allocatedRun.SetTo(newLog.AllocationGroup(), oldEnd, newEnd - oldEnd);
else
allocatedRun = newLog;
Transaction transaction(fVolume, 0);
status = allocator.AllocateBlockRun(transaction, allocatedRun);
if (status != B_OK) {
FATAL(("MoveLog: Could not allocate space to move log area!\n"));
return status;
}
status = transaction.Done();
if (status != B_OK)
return status;
}
RecursiveLocker locker(fLock);
status = _FlushLog(true, true, true);
if (status != B_OK)
return status;
MutexLocker volumeLock(fVolume->Lock());
// update references to the log location and size
fVolume->SuperBlock().log_blocks = newLog;
status = fVolume->WriteSuperBlock();
if (status != B_OK) {
fVolume->SuperBlock().log_blocks = oldLog;
// if we had to allocate some blocks, try to free them
if (!allocatedRun.IsZero()) {
Transaction transaction(fVolume, 0);
status_t freeStatus = allocator.Free(transaction, allocatedRun);
if (freeStatus == B_OK)
freeStatus = transaction.Done();
// don't really care if we fail
if (freeStatus != B_OK)
REPORT_ERROR(freeStatus);
}
return status;
}
fLogSize = newLog.Length();
fMaxTransactionSize = fLogSize / 2 - 5;
volumeLock.Unlock();
locker.Unlock();
// at this point, the log is moved and functional in its new location
// free blocks if necessary
if (newEnd < oldEnd) {
block_run runToFree = block_run::Run(0, newEnd, oldEnd - newEnd);
Transaction transaction(fVolume, 0);
status = allocator.Free(transaction, runToFree);
if (status == B_OK)
status = transaction.Done();
// we've already moved the log, no sense in failing just because we
// couldn't free a couple of blocks
if (status != B_OK)
REPORT_ERROR(status);
}
return B_OK;
}
// #pragma mark - debugger commands
@@ -41,6 +41,8 @@ public:
inline uint32 FreeLogBlocks() const;
status_t MoveLog(block_run newLog);
#ifdef BFS_DEBUGGER_COMMANDS
void Dump();
#endif
@@ -49,7 +51,8 @@ private:
bool _HasSubTransaction() const
{ return fHasSubtransaction; }
status_t _FlushLog(bool canWait, bool flushBlocks);
status_t _FlushLog(bool canWait, bool flushBlocks,
bool movingLog = false);
uint32 _TransactionSize() const;
status_t _WriteTransactionToLog();
status_t _CheckRunArray(const run_array* array);