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.
|
* 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
|
status_t
|
||||||
BlockAllocator::Free(Transaction& transaction, block_run run)
|
BlockAllocator::Free(Transaction& transaction, block_run run)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -41,11 +41,14 @@ public:
|
|||||||
status_t Allocate(Transaction& transaction, Inode* inode,
|
status_t Allocate(Transaction& transaction, Inode* inode,
|
||||||
off_t numBlocks, block_run& run,
|
off_t numBlocks, block_run& run,
|
||||||
uint16 minimum = 1);
|
uint16 minimum = 1);
|
||||||
status_t Free(Transaction& transaction, block_run run);
|
|
||||||
|
|
||||||
status_t AllocateBlocks(Transaction& transaction,
|
status_t AllocateBlocks(Transaction& transaction,
|
||||||
int32 group, uint16 start, uint16 numBlocks,
|
int32 group, uint16 start, uint16 numBlocks,
|
||||||
uint16 minimum, block_run& run);
|
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,
|
status_t Trim(uint64 offset, uint64 size,
|
||||||
uint64& trimmedSize);
|
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.
|
* 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
|
/*! 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
|
status_t
|
||||||
Journal::_FlushLog(bool canWait, bool flushBlocks)
|
Journal::_FlushLog(bool canWait, bool flushBlocks, bool movingLog)
|
||||||
{
|
{
|
||||||
status_t status = canWait ? recursive_lock_lock(&fLock)
|
status_t status = canWait ? recursive_lock_lock(&fLock)
|
||||||
: recursive_lock_trylock(&fLock);
|
: recursive_lock_trylock(&fLock);
|
||||||
if (status != B_OK)
|
if (status != B_OK)
|
||||||
return status;
|
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
|
// whoa, FlushLogAndBlocks() was called from inside a transaction
|
||||||
recursive_lock_unlock(&fLock);
|
recursive_lock_unlock(&fLock);
|
||||||
return B_OK;
|
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
|
// #pragma mark - debugger commands
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -41,6 +41,8 @@ public:
|
|||||||
|
|
||||||
inline uint32 FreeLogBlocks() const;
|
inline uint32 FreeLogBlocks() const;
|
||||||
|
|
||||||
|
status_t MoveLog(block_run newLog);
|
||||||
|
|
||||||
#ifdef BFS_DEBUGGER_COMMANDS
|
#ifdef BFS_DEBUGGER_COMMANDS
|
||||||
void Dump();
|
void Dump();
|
||||||
#endif
|
#endif
|
||||||
@@ -49,7 +51,8 @@ private:
|
|||||||
bool _HasSubTransaction() const
|
bool _HasSubTransaction() const
|
||||||
{ return fHasSubtransaction; }
|
{ return fHasSubtransaction; }
|
||||||
|
|
||||||
status_t _FlushLog(bool canWait, bool flushBlocks);
|
status_t _FlushLog(bool canWait, bool flushBlocks,
|
||||||
|
bool movingLog = false);
|
||||||
uint32 _TransactionSize() const;
|
uint32 _TransactionSize() const;
|
||||||
status_t _WriteTransactionToLog();
|
status_t _WriteTransactionToLog();
|
||||||
status_t _CheckRunArray(const run_array* array);
|
status_t _CheckRunArray(const run_array* array);
|
||||||
|
|||||||
Reference in New Issue
Block a user