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:
committed by
Axel Dörfler
parent
eba66a0e71
commit
2f76872b8e
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user