added 11 changesets to branch 'refs/remotes/ahenriksson-github/production' old head: 0e8f38ab332a8d0d43e9c03a07c076b9a8c3f759 new head: 5cbffd392ebc19ddcecc1ccc64fe20a1c36e370f ---------------------------------------------------------------------------- e125c56: Write the finished transaction to disk before writing super block 7348e62: Lock attribute directory when we move an attribute e0121e2: Change vnode ID in associated VMVnodeCache ee4521c: No need to get the DiskSystem in resizefs 9322b1e: Add a function for allocating a specific block run Intended for use by Journal::MoveLog(). 95b0703: Use AllocateBlockRun in MoveLog 8ac30e3: Allowed allocation range is part of the block allocator's state This is necessary to successfully resize file systems while data is copied to them. a938a37: Add mapping from vnode ID to block number for moved inodes 45fefb3: Don't check device size when resizing It's not our responsibility. This reverts "Calculate device size in Volume for use by resize code", and removes the check in ResizeVisitor. e8d0bc3: Added code to update the block cache size 5cbffd3: Fix deadlock when reinitializing block allocator [ ahenriksson <sausageboy@xxxxxxxxx> ] ---------------------------------------------------------------------------- 15 files changed, 416 insertions(+), 180 deletions(-) .../kernel/file_systems/bfs/BlockAllocator.cpp | 168 +++++++++++++--- .../kernel/file_systems/bfs/BlockAllocator.h | 15 +- src/add-ons/kernel/file_systems/bfs/Inode.cpp | 81 +++----- src/add-ons/kernel/file_systems/bfs/Inode.h | 18 +- src/add-ons/kernel/file_systems/bfs/Journal.cpp | 50 +++-- src/add-ons/kernel/file_systems/bfs/Journal.h | 4 +- .../kernel/file_systems/bfs/ResizeVisitor.cpp | 49 +++-- .../kernel/file_systems/bfs/ResizeVisitor.h | 1 + src/add-ons/kernel/file_systems/bfs/Volume.cpp | 159 ++++++++++++--- src/add-ons/kernel/file_systems/bfs/Volume.h | 26 ++- .../kernel/file_systems/bfs/kernel_interface.cpp | 10 +- .../kernel/file_systems/bfs/system_dependencies.h | 1 + src/bin/resizefs.cpp | 8 - src/system/kernel/cache/vnode_store.h | 3 + src/system/kernel/fs/vfs.cpp | 3 + ############################################################################ Commit: e125c56e31ac5c5168178777970a84a0faa56e8d Author: ahenriksson <sausageboy@xxxxxxxxx> Date: Wed Aug 1 13:31:00 2012 UTC Write the finished transaction to disk before writing super block ---------------------------------------------------------------------------- diff --git a/src/add-ons/kernel/file_systems/bfs/ResizeVisitor.cpp b/src/add-ons/kernel/file_systems/bfs/ResizeVisitor.cpp index 481615ed..35255d7 100644 --- a/src/add-ons/kernel/file_systems/bfs/ResizeVisitor.cpp +++ b/src/add-ons/kernel/file_systems/bfs/ResizeVisitor.cpp @@ -551,6 +551,15 @@ ResizeVisitor::_UpdateChildren(Transaction& transaction, Inode* inode, status_t ResizeVisitor::_UpdateSuperBlock(Inode* inode, off_t newInodeID) { + // we need to have our transaction written to disk before we can write + // the updated super block, otherwise we might end up with a dangling + // reference if something fails later on. + status_t status = GetVolume()->GetJournal(0)->FlushLogAndBlocks(); + if (status != B_OK) { + FATAL(("Resize: Failed to flush log before updating super block!\n")); + return status; + } + MutexLocker locker(GetVolume()->Lock()); disk_super_block& superBlock = GetVolume()->SuperBlock(); ############################################################################ Commit: 7348e621a1ae5b14f775d223e14dc517757d07c3 Author: ahenriksson <sausageboy@xxxxxxxxx> Date: Wed Aug 1 13:33:26 2012 UTC Lock attribute directory when we move an attribute ---------------------------------------------------------------------------- diff --git a/src/add-ons/kernel/file_systems/bfs/ResizeVisitor.cpp b/src/add-ons/kernel/file_systems/bfs/ResizeVisitor.cpp index 35255d7..b7fa110 100644 --- a/src/add-ons/kernel/file_systems/bfs/ResizeVisitor.cpp +++ b/src/add-ons/kernel/file_systems/bfs/ResizeVisitor.cpp @@ -385,6 +385,8 @@ ResizeVisitor::_UpdateAttributeDirectory(Transaction& transaction, Inode* inode, if (status != B_OK) return status; + attributeDirectory->WriteLockInTransaction(transaction); + attributeDirectory->Parent() = newInodeRun; return attributeDirectory->WriteBack(transaction); } ############################################################################ Commit: e0121e2886391528a2363a2d2d18d492fe815926 Author: ahenriksson <sausageboy@xxxxxxxxx> Date: Thu Aug 2 17:02:22 2012 UTC Change vnode ID in associated VMVnodeCache ---------------------------------------------------------------------------- diff --git a/src/system/kernel/cache/vnode_store.h b/src/system/kernel/cache/vnode_store.h index 3479976..b50417c 100644 --- a/src/system/kernel/cache/vnode_store.h +++ b/src/system/kernel/cache/vnode_store.h @@ -48,6 +48,9 @@ public: void VnodeDeleted() { fVnodeDeleted = true; } + void SetVnodeID(ino_t id) + { fInode = id; } + protected: virtual void DeleteObject(); diff --git a/src/system/kernel/fs/vfs.cpp b/src/system/kernel/fs/vfs.cpp index 5b2772c..3a6295c 100644 --- a/src/system/kernel/fs/vfs.cpp +++ b/src/system/kernel/fs/vfs.cpp @@ -3902,6 +3902,9 @@ change_vnode_id(fs_volume* volume, ino_t vnodeID, ino_t newID) vnode->id = newID; hash_insert(sVnodeTable, vnode); + if (vnode->cache) + ((VMVnodeCache*)vnode->cache)->SetVnodeID(newID); + return B_OK; } ############################################################################ Commit: ee4521c5b4b4a2040e06675ce599605711daf2e4 Author: ahenriksson <sausageboy@xxxxxxxxx> Date: Thu Aug 2 17:04:51 2012 UTC No need to get the DiskSystem in resizefs ---------------------------------------------------------------------------- diff --git a/src/bin/resizefs.cpp b/src/bin/resizefs.cpp index b66a3c9..32f3bc6 100644 --- a/src/bin/resizefs.cpp +++ b/src/bin/resizefs.cpp @@ -84,14 +84,6 @@ main(int argc, char** argv) return 1; } - BDiskSystem diskSystem; - status = partition->GetDiskSystem(&diskSystem); - if (status != B_OK) { - fprintf(stderr, "%s: Failed to get disk system for partition: %s\n", - kProgramName, strerror(status)); - return 1; - } - // Validate the requested size off_t validatedSize = size; ############################################################################ Commit: 9322b1ea3095a7fa42321de9fed2a0d9e51463ac Author: ahenriksson <sausageboy@xxxxxxxxx> Date: Mon Aug 6 20:20:22 2012 UTC Add a function for allocating a specific block run Intended for use by Journal::MoveLog(). ---------------------------------------------------------------------------- diff --git a/src/add-ons/kernel/file_systems/bfs/BlockAllocator.cpp b/src/add-ons/kernel/file_systems/bfs/BlockAllocator.cpp index cf935d1..30f52d6 100644 --- a/src/add-ons/kernel/file_systems/bfs/BlockAllocator.cpp +++ b/src/add-ons/kernel/file_systems/bfs/BlockAllocator.cpp @@ -1066,6 +1066,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 (!IsBlockRunInRange(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) { diff --git a/src/add-ons/kernel/file_systems/bfs/BlockAllocator.h b/src/add-ons/kernel/file_systems/bfs/BlockAllocator.h index cd494d2..4be864c 100644 --- a/src/add-ons/kernel/file_systems/bfs/BlockAllocator.h +++ b/src/add-ons/kernel/file_systems/bfs/BlockAllocator.h @@ -47,6 +47,9 @@ public: uint16 minimum, block_run& run, off_t beginBlock = 0, off_t endBlock = 0); + status_t AllocateBlockRun(Transaction& transaction, + block_run run); + status_t CheckBlocks(off_t start, off_t length, bool allocated = true, off_t* firstError = NULL); ############################################################################ Commit: 95b070339397f1c6090d28b24a4fb2cefc227d6e Author: ahenriksson <sausageboy@xxxxxxxxx> Date: Mon Aug 6 20:26:30 2012 UTC Use AllocateBlockRun in MoveLog ---------------------------------------------------------------------------- diff --git a/src/add-ons/kernel/file_systems/bfs/Journal.cpp b/src/add-ons/kernel/file_systems/bfs/Journal.cpp index 00ec40a..b89f45a 100644 --- a/src/add-ons/kernel/file_systems/bfs/Journal.cpp +++ b/src/add-ons/kernel/file_systems/bfs/Journal.cpp @@ -1110,17 +1110,11 @@ Journal::MoveLog(block_run newLog) Transaction transaction(fVolume, 0); - status = allocator.AllocateBlocks(transaction, 0, oldEnd, - allocationSize, 1, allocatedRun, oldEnd, newEnd); - if (status != B_OK) + status = allocator.AllocateBlockRun(transaction, + block_run::Run(0, oldEnd, allocationSize)); + if (status != B_OK) { + FATAL(("MoveLog: Could not allocate space to move log area!\n")); return status; - - if (allocatedRun.AllocationGroup() != 0 - || allocatedRun.Start() != oldEnd - || allocatedRun.Length() != allocationSize) { - // we couldn't allocate what we wanted, this means that we - // failed to move all data from this area - return B_ERROR; } status = transaction.Done(); ############################################################################ Commit: 8ac30e396c70e4b316f36111c3da10fcd579af0e Author: ahenriksson <sausageboy@xxxxxxxxx> Date: Mon Aug 6 20:26:52 2012 UTC Allowed allocation range is part of the block allocator's state This is necessary to successfully resize file systems while data is copied to them. ---------------------------------------------------------------------------- diff --git a/src/add-ons/kernel/file_systems/bfs/BlockAllocator.cpp b/src/add-ons/kernel/file_systems/bfs/BlockAllocator.cpp index 30f52d6..2c93c2d 100644 --- a/src/add-ons/kernel/file_systems/bfs/BlockAllocator.cpp +++ b/src/add-ons/kernel/file_systems/bfs/BlockAllocator.cpp @@ -505,7 +505,9 @@ AllocationGroup::Free(Transaction& transaction, uint16 start, int32 length) BlockAllocator::BlockAllocator(Volume* volume) : fVolume(volume), - fGroups(NULL) + fGroups(NULL), + fBeginBlock(0), + fEndBlock(0) { recursive_lock_init(&fLock, "bfs allocator"); } @@ -734,26 +736,61 @@ BlockAllocator::Uninitialize() } -/*! Tries to allocate between \a minimum, and \a maximum blocks in the range - from \a beginBlock up to but not including \a endBlock, starting at group - \a groupIndex with offset \a start. The resulting allocation is put into - \a run. +/*! Specifies the range in which blocks will be allocated, starting from + \a beginBlock, up to but not including \a endBlock. - An \a endBlock value of 0 means no upper limit on the allowed allocation - range. + \a beginBlock and \a endBlock values of 0 means that no limits on the + allowed allocation range are imposed. +*/ +void +BlockAllocator::SetRange(off_t beginBlock, off_t endBlock) +{ + RecursiveLocker lock(fLock); + + fBeginBlock = beginBlock; + fEndBlock = endBlock; +} + + +bool +BlockAllocator::IsBlockRunInRange(block_run run) const +{ + if (fBeginBlock == 0 && fEndBlock == 0) + return true; + + off_t runStart = fVolume->ToBlock(run); + return runStart >= fBeginBlock && runStart + run.Length() <= fEndBlock; +} + + +bool +BlockAllocator::IsBlockRunOutsideRange(block_run run) const +{ + if (fBeginBlock == 0 && fEndBlock == 0) + return false; + + // note that this is not the opposite of _IsBlockRunInRange, as we only + // return true if the block run is completely outside the range. + off_t runStart = fVolume->ToBlock(run); + return runStart + run.Length() <= fBeginBlock || runStart >= fEndBlock; +} + + +/*! Tries to allocate between \a minimum, and \a maximum blocks, starting at + group \a groupIndex with offset \a start. The resulting allocation is put + into \a run. The number of allocated blocks is always a multiple of \a minimum which has to be a power of two value. */ status_t BlockAllocator::AllocateBlocks(Transaction& transaction, int32 groupIndex, - uint16 start, uint16 maximum, uint16 minimum, block_run& run, - off_t beginBlock, off_t endBlock) + uint16 start, uint16 maximum, uint16 minimum, block_run& run) { if (maximum == 0) return B_BAD_VALUE; - if (endBlock > fVolume->NumBlocks()) + if (fEndBlock > fVolume->NumBlocks()) return B_BAD_VALUE; FUNCTION_START(("group = %ld, start = %u, maximum = %u, minimum = %u\n", @@ -766,21 +803,21 @@ BlockAllocator::AllocateBlocks(Transaction& transaction, int32 groupIndex, // Express the allowed allocation range in terms of allocation groups and // offsets. - int32 firstAllowedGroup = beginBlock / bitsPerFullBlock; - uint16 firstGroupBegin = beginBlock % bitsPerFullBlock; + int32 firstAllowedGroup = fBeginBlock / bitsPerFullBlock; + uint16 firstGroupBegin = fBeginBlock % bitsPerFullBlock; int32 lastAllowedGroup; int32 lastGroupEnd; - if (endBlock == 0) { + if (fEndBlock == 0) { lastAllowedGroup = fNumGroups - 1; lastGroupEnd = fGroups[lastAllowedGroup].NumBits(); } else { - // If endBlock is the first block of an allocation group, the last + // If fEndBlock is the first block of an allocation group, the last // allowed group is the previous, and the end block offset is // bitsPerFullBlock. - lastAllowedGroup = (endBlock - 1) / bitsPerFullBlock; - lastGroupEnd = endBlock % bitsPerFullBlock; + lastAllowedGroup = (fEndBlock - 1) / bitsPerFullBlock; + lastGroupEnd = fEndBlock % bitsPerFullBlock; if (lastGroupEnd == 0) lastGroupEnd = bitsPerFullBlock; } @@ -965,8 +1002,8 @@ BlockAllocator::AllocateBlocks(Transaction& transaction, int32 groupIndex, run.start = HOST_ENDIAN_TO_BFS_INT16(bestStart); run.length = HOST_ENDIAN_TO_BFS_INT16(bestLength); - ASSERT(fVolume->ToBlock(run) >= beginBlock); - ASSERT(endBlock == 0 || fVolume->ToBlock(run) + run.Length() <= endBlock); + ASSERT(fVolume->ToBlock(run) >= fBeginBlock); + ASSERT(fEndBlock == 0 || fVolume->ToBlock(run) + run.Length() <= fEndBlock); fVolume->SuperBlock().used_blocks = HOST_ENDIAN_TO_BFS_INT64(fVolume->UsedBlocks() + bestLength); @@ -1007,8 +1044,7 @@ BlockAllocator::AllocateForInode(Transaction& transaction, status_t BlockAllocator::Allocate(Transaction& transaction, Inode* inode, - off_t numBlocks, block_run& run, uint16 minimum, off_t beginBlock, - off_t endBlock) + off_t numBlocks, block_run& run, uint16 minimum) { if (numBlocks <= 0) return B_ERROR; @@ -1061,12 +1097,12 @@ BlockAllocator::Allocate(Transaction& transaction, Inode* inode, group = inode->BlockRun().AllocationGroup() + 1; } - return AllocateBlocks(transaction, group, start, numBlocks, minimum, run, - beginBlock, endBlock); + return AllocateBlocks(transaction, group, start, numBlocks, minimum, run); } -/*! Attempts to allocate a specific block run. +/*! Attempts to allocate a specific block run. + Does not check if the block run is within the current allocation range. */ status_t BlockAllocator::AllocateBlockRun(Transaction& transaction, block_run run) @@ -1076,9 +1112,6 @@ BlockAllocator::AllocateBlockRun(Transaction& transaction, block_run run) if (run.AllocationGroup() >= fNumGroups) return B_BAD_VALUE; - if (!IsBlockRunInRange(run)) - return B_DEVICE_FULL; - uint32 bitsPerBlock = fVolume->BlockSize() << 3; AllocationGroup& group = fGroups[run.AllocationGroup()]; diff --git a/src/add-ons/kernel/file_systems/bfs/BlockAllocator.h b/src/add-ons/kernel/file_systems/bfs/BlockAllocator.h index 4be864c..3de7212 100644 --- a/src/add-ons/kernel/file_systems/bfs/BlockAllocator.h +++ b/src/add-ons/kernel/file_systems/bfs/BlockAllocator.h @@ -33,19 +33,21 @@ public: void Uninitialize(); + void SetRange(off_t beginBlock, off_t endBlock); + bool IsBlockRunInRange(block_run run) const; + bool IsBlockRunOutsideRange(block_run run) const; + status_t AllocateForInode(Transaction& transaction, const block_run* parent, mode_t type, block_run& run); status_t Allocate(Transaction& transaction, Inode* inode, off_t numBlocks, block_run& run, - uint16 minimum = 1, off_t beginBlock = 0, - off_t endBlock = 0); + 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, - off_t beginBlock = 0, off_t endBlock = 0); + uint16 minimum, block_run& run); status_t AllocateBlockRun(Transaction& transaction, block_run run); @@ -80,6 +82,9 @@ private: int32 fNumGroups; uint32 fBlocksPerGroup; uint32 fNumBlocks; + + off_t fBeginBlock; + off_t fEndBlock; }; #ifdef BFS_DEBUGGER_COMMANDS diff --git a/src/add-ons/kernel/file_systems/bfs/Inode.cpp b/src/add-ons/kernel/file_systems/bfs/Inode.cpp index 167821d..64b2a2a 100644 --- a/src/add-ons/kernel/file_systems/bfs/Inode.cpp +++ b/src/add-ons/kernel/file_systems/bfs/Inode.cpp @@ -1833,13 +1833,13 @@ Inode::FillGapWithZeros(off_t pos, off_t newSize) */ status_t Inode::_AllocateBlockArray(Transaction& transaction, block_run& run, - size_t length, bool variableSize, off_t beginBlock, off_t endBlock) + size_t length, bool variableSize) { if (!run.IsZero()) return B_BAD_VALUE; status_t status = fVolume->Allocate(transaction, this, length, run, - variableSize ? 1 : length, beginBlock, endBlock); + variableSize ? 1 : length); if (status != B_OK) return status; @@ -1867,8 +1867,7 @@ Inode::_AllocateBlockArray(Transaction& transaction, block_run& run, */ status_t Inode::_AddBlockRun(Transaction& transaction, data_stream* dataStream, - block_run run, off_t targetSize, int32* rest, off_t beginBlock, - off_t endBlock) + block_run run, off_t targetSize, int32* rest) { status_t status; @@ -1920,7 +1919,7 @@ Inode::_AddBlockRun(Transaction& transaction, data_stream* dataStream, // if there is no indirect block yet, create one if (dataStream->indirect.IsZero()) { status = _AllocateBlockArray(transaction, dataStream->indirect, - NUM_ARRAY_BLOCKS, true, beginBlock, endBlock); + NUM_ARRAY_BLOCKS, true); if (status != B_OK) return status; @@ -2001,7 +2000,7 @@ Inode::_AddBlockRun(Transaction& transaction, data_stream* dataStream, if (dataStream->double_indirect.IsZero()) { status = _AllocateBlockArray(transaction, dataStream->double_indirect, _DoubleIndirectBlockLength(), - false, beginBlock, endBlock); + false); if (status != B_OK) return status; @@ -2051,8 +2050,7 @@ Inode::_AddBlockRun(Transaction& transaction, data_stream* dataStream, status = _AllocateBlockArray(transaction, array[indirectIndex % runsPerBlock], - dataStream->double_indirect.Length(), false, - beginBlock, endBlock); + dataStream->double_indirect.Length(), false); if (status != B_OK) return status; } @@ -2669,8 +2667,7 @@ Inode::Sync() */ status_t Inode::_WriteBufferedRuns(Transaction& transaction, BlockRunBuffer& buffer, - data_stream* dataStream, off_t targetSize, bool flush, off_t beginBlock, - off_t endBlock) + data_stream* dataStream, off_t targetSize, bool flush) { status_t status; off_t doubleIndirectBlockLength = _DoubleIndirectBlockLength(); @@ -2696,8 +2693,7 @@ Inode::_WriteBufferedRuns(Transaction& transaction, BlockRunBuffer& buffer, } status = fVolume->Allocator().AllocateBlocks(transaction, 0, 0, - buffer.NumBlocks(), doubleIndirectBlockLength, run, - beginBlock, endBlock); + buffer.NumBlocks(), doubleIndirectBlockLength, run); if (status != B_OK) return status; @@ -2710,7 +2706,7 @@ Inode::_WriteBufferedRuns(Transaction& transaction, BlockRunBuffer& buffer, blocksToWrite = run.Length(); } else { status = fVolume->Allocator().AllocateBlocks(transaction, 0, 0, - buffer.NumBlocks(), 1, run, beginBlock, endBlock); + buffer.NumBlocks(), 1, run); if (status != B_OK) return status; @@ -2719,8 +2715,7 @@ Inode::_WriteBufferedRuns(Transaction& transaction, BlockRunBuffer& buffer, // add the run to the data stream int32 rest; - status = _AddBlockRun(transaction, dataStream, run, targetSize, - &rest, beginBlock, endBlock); + status = _AddBlockRun(transaction, dataStream, run, targetSize, &rest); if (status != B_OK) return status; @@ -2831,30 +2826,11 @@ Inode::_GetNextBlockRun(off_t& position, block_run& run) const } -bool -Inode::_IsBlockRunInRange(block_run run, off_t beginBlock, off_t endBlock) const -{ - off_t runStart = GetVolume()->ToBlock(run); - return runStart >= beginBlock && runStart + run.Length() <= endBlock; -} - - -bool -Inode::_IsBlockRunOutsideRange(block_run run, off_t beginBlock, off_t endBlock) - const -{ - // note that this is not the opposite of _IsBlockRunInRange, as we only - // return true if the block run is completely outside the range. - off_t runStart = GetVolume()->ToBlock(run); - return runStart + run.Length() <= beginBlock || runStart >= endBlock; -} - - -/*! Move the file stream to lie completely in the range from \a beginBlock - up to but not including \a endBlock. +/*! Move the file stream to lie completely in the allocation range specified + in the block allocator. */ status_t -Inode::MoveStream(off_t beginBlock, off_t endBlock) +Inode::MoveStream() { // We move the stream by rebuilding it from scratch. block_runs which // can be reused are simply added to the result stream (newStream) as @@ -2884,6 +2860,8 @@ Inode::MoveStream(off_t beginBlock, off_t endBlock) if (status != B_OK) return status; + BlockAllocator& allocator = fVolume->Allocator(); + Stack<block_run> runsToFree; // no particular reason for using a stack, we just need something to // store block_run's in @@ -2902,9 +2880,9 @@ Inode::MoveStream(off_t beginBlock, off_t endBlock) if (status != B_OK) return status; - if (_IsBlockRunInRange(run, beginBlock, endBlock)) { + if (allocator.IsBlockRunInRange(run)) { status = _WriteBufferedRuns(transaction, buffer, &newStream, - Size(), false, beginBlock, endBlock); + Size(), false); if (status != B_OK) return status; @@ -2914,7 +2892,7 @@ Inode::MoveStream(off_t beginBlock, off_t endBlock) if (buffer.IsEmpty()) { int32 rest; status = _AddBlockRun(transaction, &newStream, run, Size(), - &rest, beginBlock, endBlock); + &rest); if (status != B_OK) return status; @@ -2931,13 +2909,13 @@ Inode::MoveStream(off_t beginBlock, off_t endBlock) // We can't reuse this block_run, add it to the block_run buffer. if (buffer.IsFull()) { status = _WriteBufferedRuns(transaction, buffer, &newStream, - Size(), false, beginBlock, endBlock); + Size(), false); if (status != B_OK) return status; } buffer.PutRun(run); - if (_IsBlockRunOutsideRange(run, beginBlock, endBlock)) { + if (allocator.IsBlockRunOutsideRange(run)) { // The block run was outside of the allowed range, which means // that we can free it now without running the risk of // reallocating it and overwriting its content. @@ -2953,8 +2931,7 @@ Inode::MoveStream(off_t beginBlock, off_t endBlock) } // write all blocks remaining in the buffer to disk - status = _WriteBufferedRuns(transaction, buffer, &newStream, Size(), true, - beginBlock, endBlock); + status = _WriteBufferedRuns(transaction, buffer, &newStream, Size(), true); if (status != B_OK) return status; @@ -2979,25 +2956,27 @@ Inode::MoveStream(off_t beginBlock, off_t endBlock) } -/*! Check if the file stream is in the range from \a beginBlock up to - but not including \endBlock. +/*! Check if the file stream is in the allocation range specified in the + block allocator. */ status_t -Inode::StreamInRange(off_t beginBlock, off_t endBlock, bool& inRange) const +Inode::StreamInRange(bool& inRange) const { inRange = false; // now we can simply return if we find a run which is not in the range + + BlockAllocator& allocator = fVolume->Allocator(); // check that the indirection blocks are in the allowed range const data_stream* data = &fNode.data; if (!data->indirect.IsZero() - && !_IsBlockRunInRange(data->indirect, beginBlock, endBlock)) { + && !allocator.IsBlockRunInRange(data->indirect)) { return B_OK; } if (!data->double_indirect.IsZero()) { - if (!_IsBlockRunInRange(data->double_indirect, beginBlock, endBlock)) + if (!allocator.IsBlockRunInRange(data->double_indirect)) return B_OK; int32 runsPerBlock = fVolume->BlockSize() / sizeof(block_run); @@ -3016,7 +2995,7 @@ Inode::StreamInRange(off_t beginBlock, off_t endBlock, bool& inRange) const break; } - if (!_IsBlockRunInRange(runArray[i], beginBlock, endBlock)) + if (!allocator.IsBlockRunInRange(runArray[i])) return B_OK; } @@ -3036,7 +3015,7 @@ Inode::StreamInRange(off_t beginBlock, off_t endBlock, bool& inRange) const if (status != B_OK) return status; - if (!_IsBlockRunInRange(run, beginBlock, endBlock)) + if (!allocator.IsBlockRunInRange(run)) return B_OK; } diff --git a/src/add-ons/kernel/file_systems/bfs/Inode.h b/src/add-ons/kernel/file_systems/bfs/Inode.h index 15580f8..fa02dde 100644 --- a/src/add-ons/kernel/file_systems/bfs/Inode.h +++ b/src/add-ons/kernel/file_systems/bfs/Inode.h @@ -153,9 +153,8 @@ public: status_t TrimPreallocation(Transaction& transaction); bool NeedsTrimming() const; - status_t MoveStream(off_t beginBlock, off_t endBlock); - status_t StreamInRange(off_t beginBlock, off_t endBlock, - bool& inRange) const; + status_t MoveStream(); + status_t StreamInRange(bool& inRange) const; status_t Free(Transaction& transaction); status_t Sync(); @@ -251,32 +250,25 @@ private: off_t size, off_t& offset, off_t& max); status_t _AllocateBlockArray(Transaction& transaction, block_run& run, size_t length, - bool variableSize = false, - off_t beginBlock = 0, off_t endBlock = 0); + bool variableSize = false); status_t _GrowStream(Transaction& transaction, off_t size); status_t _ShrinkStream(Transaction& transaction, off_t size); status_t _AddBlockRun(Transaction& transaction, data_stream* dataStream, block_run run, - off_t targetSize, int32* rest = NULL, - off_t beginBlock = 0, off_t endBlock = 0); + off_t targetSize, int32* rest = NULL); // moving the data stream status_t _WriteBufferedRuns(Transaction& transaction, BlockRunBuffer& buffer, data_stream* dataStream, off_t targetSize, - bool flush = false, off_t beginBlock = 0, - off_t endBlock = 0); + bool flush = false); status_t _FreeIndirectBlocks(Transaction& transaction, data_stream *dataStream); status_t _GetNextBlockRun(off_t& position, block_run& run) const; - bool _IsBlockRunInRange(block_run run, - off_t beginBlock, off_t endBlock) const; - bool _IsBlockRunOutsideRange(block_run run, - off_t beginBlock, off_t endBlock) const; private: rw_lock fLock; diff --git a/src/add-ons/kernel/file_systems/bfs/ResizeVisitor.cpp b/src/add-ons/kernel/file_systems/bfs/ResizeVisitor.cpp index b7fa110..47c05b0 100644 --- a/src/add-ons/kernel/file_systems/bfs/ResizeVisitor.cpp +++ b/src/add-ons/kernel/file_systems/bfs/ResizeVisitor.cpp @@ -22,6 +22,13 @@ ResizeVisitor::ResizeVisitor(Volume* volume) } +ResizeVisitor::~ResizeVisitor() +{ + // reset the allocation range + GetVolume()->Allocator().SetRange(0, 0); +} + + status_t ResizeVisitor::Resize(off_t size, disk_job_id job) { @@ -50,6 +57,9 @@ ResizeVisitor::Resize(off_t size, disk_job_id job) Start(VISIT_REGULAR | VISIT_INDICES | VISIT_REMOVED | VISIT_ATTRIBUTE_DIRECTORIES); + GetVolume()->Allocator().SetRange(fBeginBlock, fEndBlock); + // this is reset in our destructor + // move file system data out of the way while (true) { fError = false; @@ -151,7 +161,7 @@ ResizeVisitor::VisitInode(Inode* inode, const char* treeName) // move the stream if necessary bool inRange; - status = inode->StreamInRange(fBeginBlock, fEndBlock, inRange); + status = inode->StreamInRange(inRange); if (status != B_OK) { FATAL(("Resize: Failed to check file stream, inode %" B_PRIdINO ", \"%s\"!\n", inode->ID(), name)); @@ -160,7 +170,7 @@ ResizeVisitor::VisitInode(Inode* inode, const char* treeName) } if (!inRange) { - status = inode->MoveStream(fBeginBlock, fEndBlock); + status = inode->MoveStream(); if (status != B_OK) { FATAL(("Resize: Failed to move file stream, inode %" B_PRIdINO ", \"%s\"!\n", inode->ID(), name)); @@ -590,7 +600,7 @@ ResizeVisitor::_MoveInode(Inode* inode, off_t& newInodeID, const char* treeName) block_run run; status_t status = GetVolume()->Allocator().AllocateBlocks(transaction, 0, 0, - 1, 1, run, fBeginBlock, fEndBlock); + 1, 1, run); // TODO: use a hint, maybe old position % new volume size? // stuff that originally was in the beginning should probably // stay close to it diff --git a/src/add-ons/kernel/file_systems/bfs/ResizeVisitor.h b/src/add-ons/kernel/file_systems/bfs/ResizeVisitor.h index 41f5535..69e8328 100644 --- a/src/add-ons/kernel/file_systems/bfs/ResizeVisitor.h +++ b/src/add-ons/kernel/file_systems/bfs/ResizeVisitor.h @@ -19,6 +19,7 @@ class Transaction; class ResizeVisitor : public FileSystemVisitor { public: ResizeVisitor(Volume* volume); + virtual ~ResizeVisitor(); status_t Resize(off_t size, disk_job_id job); diff --git a/src/add-ons/kernel/file_systems/bfs/Volume.h b/src/add-ons/kernel/file_systems/bfs/Volume.h index 6e877b7..d1c6a0d 100644 --- a/src/add-ons/kernel/file_systems/bfs/Volume.h +++ b/src/add-ons/kernel/file_systems/bfs/Volume.h @@ -116,8 +116,7 @@ public: block_run& run); status_t Allocate(Transaction& transaction, Inode* inode, off_t numBlocks, block_run& run, - uint16 minimum = 1, off_t beginBlock = 0, - off_t endBlock = 0); + uint16 minimum = 1); status_t Free(Transaction& transaction, block_run run); void SetCheckingThread(thread_id thread) { fCheckingThread = thread; } @@ -222,10 +221,10 @@ Volume::AllocateForInode(Transaction& transaction, const block_run* parent, inline status_t Volume::Allocate(Transaction& transaction, Inode* inode, off_t numBlocks, - block_run& run, uint16 minimum, off_t beginBlock, off_t endBlock) + block_run& run, uint16 minimum) { return fBlockAllocator.Allocate(transaction, inode, numBlocks, run, - minimum, beginBlock, endBlock); + minimum); } ############################################################################ Commit: a938a37bddc195b3765e7f31dfac27b5375a69a7 Author: ahenriksson <sausageboy@xxxxxxxxx> Date: Thu Aug 2 17:09:15 2012 UTC Add mapping from vnode ID to block number for moved inodes ---------------------------------------------------------------------------- diff --git a/src/add-ons/kernel/file_systems/bfs/BlockAllocator.cpp b/src/add-ons/kernel/file_systems/bfs/BlockAllocator.cpp index 2c93c2d..e310ddf 100644 --- a/src/add-ons/kernel/file_systems/bfs/BlockAllocator.cpp +++ b/src/add-ons/kernel/file_systems/bfs/BlockAllocator.cpp @@ -785,7 +785,7 @@ BlockAllocator::IsBlockRunOutsideRange(block_run run) const */ status_t BlockAllocator::AllocateBlocks(Transaction& transaction, int32 groupIndex, - uint16 start, uint16 maximum, uint16 minimum, block_run& run) + uint16 start, uint16 maximum, uint16 minimum, block_run& run, bool inode) { if (maximum == 0) return B_BAD_VALUE; @@ -799,6 +799,16 @@ BlockAllocator::AllocateBlocks(Transaction& transaction, int32 groupIndex, AllocationBlock cached(fVolume); RecursiveLocker lock(fLock); + bool checkMovedInodes = false; + ReadLocker movedInodesLocker; + if (inode) { + movedInodesLocker.SetTo(fVolume->MovedInodesLock(), false); + + // don't check for moved inodes unless the volume actually has them + if (fVolume->HasMovedInodes()) + checkMovedInodes = true; + } + uint32 bitsPerFullBlock = fVolume->BlockSize() << 3; // Express the allowed allocation range in terms of allocation groups and @@ -854,7 +864,7 @@ BlockAllocator::AllocateBlocks(Transaction& transaction, int32 groupIndex, if (start < group.fFirstFree) start = group.fFirstFree; - if (group.fLargestValid) { + if (group.fLargestValid && !checkMovedInodes) { if (group.fLargestLength < bestLength) continue; @@ -884,7 +894,8 @@ BlockAllocator::AllocateBlocks(Transaction& transaction, int32 groupIndex, int32 groupLargestStart = -1; int32 groupLargestLength = -1; int32 currentBit = start; - bool canFindGroupLargest = start == 0 && end == group.NumBits(); + bool canFindGroupLargest = start == 0 && end == group.NumBits() + && !checkMovedInodes; // If end is the first bit in a block, the last block considered is the // previous one, and the end bit is bitsPerFullBlock. @@ -908,7 +919,22 @@ BlockAllocator::AllocateBlocks(Transaction& transaction, int32 groupIndex, // find a block large enough to hold the allocation for (uint32 bit = start % bitsPerFullBlock; bit < endBit; bit++) { - if (!cached.IsUsed(bit)) { + bool isUsed = cached.IsUsed(bit); + + // if we're allocating space for an inode, we treat blocks + // which used to have moved inodes as used + if (checkMovedInodes && !isUsed) { + ino_t currentBlock = fVolume->ToBlock( + block_run::Run(groupIndex, currentBit, 1)); + + // while it might seem excessive to do a hash lookup for + // every bit we check, an inode only requires a single + // block, so we're unlikely to do this more than once, or + // a couple of times at most + isUsed = fVolume->WasMovedInode(currentBlock); + } + + if (!isUsed) { if (currentLength == 0) { // start new range currentStart = currentBit; @@ -1038,7 +1064,7 @@ BlockAllocator::AllocateForInode(Transaction& transaction, if ((type & (S_DIRECTORY | S_INDEX_DIR | S_ATTR_DIR)) == S_DIRECTORY) group += 8; - return AllocateBlocks(transaction, group, 0, 1, 1, run); + return AllocateBlocks(transaction, group, 0, 1, 1, run, true); } diff --git a/src/add-ons/kernel/file_systems/bfs/BlockAllocator.h b/src/add-ons/kernel/file_systems/bfs/BlockAllocator.h index 3de7212..c8bf937 100644 --- a/src/add-ons/kernel/file_systems/bfs/BlockAllocator.h +++ b/src/add-ons/kernel/file_systems/bfs/BlockAllocator.h @@ -47,7 +47,8 @@ public: status_t AllocateBlocks(Transaction& transaction, int32 group, uint16 start, uint16 numBlocks, - uint16 minimum, block_run& run); + uint16 minimum, block_run& run, + bool inode = false); status_t AllocateBlockRun(Transaction& transaction, block_run run); diff --git a/src/add-ons/kernel/file_systems/bfs/ResizeVisitor.cpp b/src/add-ons/kernel/file_systems/bfs/ResizeVisitor.cpp index 47c05b0..fb1329e 100644 --- a/src/add-ons/kernel/file_systems/bfs/ResizeVisitor.cpp +++ b/src/add-ons/kernel/file_systems/bfs/ResizeVisitor.cpp @@ -129,6 +129,8 @@ ResizeVisitor::VisitInode(Inode* inode, const char* treeName) // start by moving the inode so we can place the stream close to it // if possible if (inodeBlock < fBeginBlock || inodeBlock >= fEndBlock) { + WriteLocker movedInodesLocker(GetVolume()->MovedInodesLock()); + status = mark_vnode_busy(GetVolume()->FSVolume(), inode->ID(), true); ino_t oldInodeID = inode->ID(); @@ -600,7 +602,7 @@ ResizeVisitor::_MoveInode(Inode* inode, off_t& newInodeID, const char* treeName) block_run run; status_t status = GetVolume()->Allocator().AllocateBlocks(transaction, 0, 0, - 1, 1, run); + 1, 1, run, true); // TODO: use a hint, maybe old position % new volume size? // stuff that originally was in the beginning should probably // stay close to it @@ -661,5 +663,11 @@ ResizeVisitor::_MoveInode(Inode* inode, off_t& newInodeID, const char* treeName) } } + status = GetVolume()->AddMovedInode(inode->ID(), newInodeID); + if (status != B_OK) { + FATAL(("_MoveInode: Could not add inode to vnodeID -> inodeID map!\n")); + return status; + } + return B_OK; } diff --git a/src/add-ons/kernel/file_systems/bfs/Volume.cpp b/src/add-ons/kernel/file_systems/bfs/Volume.cpp index a04eaab..5e33044 100644 --- a/src/add-ons/kernel/file_systems/bfs/Volume.cpp +++ b/src/add-ons/kernel/file_systems/bfs/Volume.cpp @@ -251,15 +251,20 @@ Volume::Volume(fs_volume* volume) fDirtyCachedBlocks(0), fFlags(0), fCheckingThread(-1), - fCheckVisitor(NULL) + fCheckVisitor(NULL), + fMovedInodes(NULL) { mutex_init(&fLock, "bfs volume"); mutex_init(&fQueryLock, "bfs queries"); + rw_lock_init(&fMovedInodesLock, "bfs moved inodes"); } Volume::~Volume() { + delete fMovedInodes; + + rw_lock_destroy(&fMovedInodesLock); mutex_destroy(&fQueryLock); mutex_destroy(&fLock); } @@ -511,6 +516,65 @@ Volume::CreateIndicesRoot(Transaction& transaction) status_t +Volume::AddMovedInode(ino_t oldID, ino_t newID) +{ + ASSERT_WRITE_LOCKED_RW_LOCK(&fMovedInodesLock); + + if (fMovedInodes == NULL) + fMovedInodes = new(std::nothrow) InodeMap; + + if (fMovedInodes == NULL) + return B_NO_MEMORY; + + return fMovedInodes->Put(oldID, newID); +} + + +bool +Volume::HasMovedInodes() const +{ + return fMovedInodes != NULL; +} + + +InodeMap::Iterator +Volume::MovedInodesIterator() const +{ + ASSERT(fMovedInodes != NULL); + return fMovedInodes->GetIterator(); +} + + +ino_t +Volume::ResolveVnodeID(ino_t vnodeID) +{ + ReadLocker locker(fMovedInodesLock); + + // no inodes have been moved + if (fMovedInodes == NULL) + return vnodeID; + + ino_t* inodeID; + + // has this inode been moved? + if (fMovedInodes->Get(vnodeID, inodeID)) + return *inodeID; + + return vnodeID; +} + + +bool +Volume::WasMovedInode(ino_t blockNumber) +{ + ASSERT(fMovedInodes != NULL); + ReadLocker locker(fMovedInodesLock); + + return fMovedInodes->ContainsKey(blockNumber); +} + + +status_t Volume::CreateVolumeID(Transaction& transaction) { Attribute attr(fRootNode); diff --git a/src/add-ons/kernel/file_systems/bfs/Volume.h b/src/add-ons/kernel/file_systems/bfs/Volume.h index d1c6a0d..e22d4b7 100644 --- a/src/add-ons/kernel/file_systems/bfs/Volume.h +++ b/src/add-ons/kernel/file_systems/bfs/Volume.h @@ -29,6 +29,8 @@ enum volume_initialize_flags { typedef DoublyLinkedList<Inode> InodeList; +typedef HashMap<HashKey64<ino_t>, ino_t> InodeMap; + class Volume { public: @@ -106,6 +108,15 @@ public: InodeList& RemovedInodes() { return fRemovedInodes; } // This list is guarded by the transaction lock + // moved inodes + rw_lock& MovedInodesLock() { return fMovedInodesLock; } + status_t AddMovedInode(ino_t oldID, ino_t newID); + bool HasMovedInodes() const; + InodeMap::Iterator MovedInodesIterator() const; + + ino_t ResolveVnodeID(ino_t vnodeID); + bool WasMovedInode(ino_t blockNumber); + // block bitmap BlockAllocator& Allocator(); status_t AllocateForInode(Transaction& transaction, @@ -185,6 +196,9 @@ protected: ::CheckVisitor* fCheckVisitor; InodeList fRemovedInodes; + + rw_lock fMovedInodesLock; + InodeMap* fMovedInodes; }; diff --git a/src/add-ons/kernel/file_systems/bfs/kernel_interface.cpp b/src/add-ons/kernel/file_systems/bfs/kernel_interface.cpp index a191757..8a45fc3 100644 --- a/src/add-ons/kernel/file_systems/bfs/kernel_interface.cpp +++ b/src/add-ons/kernel/file_systems/bfs/kernel_interface.cpp @@ -269,12 +269,20 @@ bfs_sync(fs_volume* _volume) /*! Reads in the node from disk and creates an inode object from it. */ static status_t -bfs_get_vnode(fs_volume* _volume, ino_t id, fs_vnode* _node, int* _type, +bfs_get_vnode(fs_volume* _volume, ino_t _id, fs_vnode* _node, int* _type, uint32* _flags, bool reenter) { //FUNCTION_START(("ino_t = %Ld\n", id)); Volume* volume = (Volume*)_volume->private_volume; + ino_t id = volume->ResolveVnodeID(_id); + if (id != _id) { + // TODO: we return an error here for now + INFORM(("Denying access to remapped inode, old ID=%" B_PRIdINO + ", new ID=%" B_PRIdINO "\n", _id, id)); + return B_ERROR; + } + // first inode may be after the log area, we don't go through // the hassle and try to load an earlier block from disk if (id < volume->ToBlock(volume->Log()) + volume->Log().Length() diff --git a/src/add-ons/kernel/file_systems/bfs/system_dependencies.h b/src/add-ons/kernel/file_systems/bfs/system_dependencies.h index 6e89896..1b27fea 100644 --- a/src/add-ons/kernel/file_systems/bfs/system_dependencies.h +++ b/src/add-ons/kernel/file_systems/bfs/system_dependencies.h @@ -16,6 +16,7 @@ #else // !BFS_SHELL #include <AutoDeleter.h> +#include <shared/HashMap.h> #include <util/AutoLock.h> #include <util/DoublyLinkedList.h> #include <util/kernel_cpp.h> ############################################################################ Commit: 45fefb34d31080190ba4107f7865236650bd24e8 Author: ahenriksson <sausageboy@xxxxxxxxx> Date: Thu Aug 2 17:43:37 2012 UTC Don't check device size when resizing It's not our responsibility. This reverts "Calculate device size in Volume for use by resize code", and removes the check in ResizeVisitor. ---------------------------------------------------------------------------- diff --git a/src/add-ons/kernel/file_systems/bfs/ResizeVisitor.cpp b/src/add-ons/kernel/file_systems/bfs/ResizeVisitor.cpp index fb1329e..eef632e 100644 --- a/src/add-ons/kernel/file_systems/bfs/ResizeVisitor.cpp +++ b/src/add-ons/kernel/file_systems/bfs/ResizeVisitor.cpp @@ -262,15 +262,6 @@ ResizeVisitor::_IsResizePossible(off_t size) return B_BAD_VALUE; } - status_t status = GetVolume()->UpdateDeviceSize(); - if (status != B_OK) - return status; - - if (GetVolume()->DeviceSize() < size) { - FATAL(("Resize: Device too small for new size\n")); - return B_BAD_VALUE; - } - if (GetVolume()->UsedBlocks() > fNumBlocks) { FATAL(("Resize: Not enough free space for resize!\n")); return B_BAD_VALUE; diff --git a/src/add-ons/kernel/file_systems/bfs/Volume.cpp b/src/add-ons/kernel/file_systems/bfs/Volume.cpp index 5e33044..ae2fce5 100644 --- a/src/add-ons/kernel/file_systems/bfs/Volume.cpp +++ b/src/add-ons/kernel/file_systems/bfs/Volume.cpp @@ -46,6 +46,8 @@ public: int Mode() const { return fMode; } bool IsReadOnly() const { return _IsReadOnly(fMode); } + status_t GetSize(off_t* _size, uint32* _blockSize = NULL); + private: static bool _IsReadOnly(int mode) { return (mode & O_RWMASK) == O_RDONLY;} @@ -154,6 +156,38 @@ DeviceOpener::Keep() } +/*! Returns the size of the device in bytes. It uses B_GET_GEOMETRY + to compute the size, or fstat() if that failed. +*/ +status_t +DeviceOpener::GetSize(off_t* _size, uint32* _blockSize) +{ + device_geometry geometry; + if (ioctl(fDevice, B_GET_GEOMETRY, &geometry) < 0) { + // maybe it's just a file + struct stat stat; + if (fstat(fDevice, &stat) < 0) + return B_ERROR; + + if (_size) + *_size = stat.st_size; + if (_blockSize) // that shouldn't cause us any problems + *_blockSize = 512; + + return B_OK; + } + + if (_size) { + *_size = 1LL * geometry.head_count * geometry.cylinder_count + * geometry.sectors_per_track * geometry.bytes_per_sector; + } + if (_blockSize) + *_blockSize = geometry.bytes_per_sector; + + return B_OK; +} + + // #pragma mark - @@ -328,11 +362,10 @@ Volume::Mount(const char* deviceName, uint32 flags) fAllocationGroupShift = fSuperBlock.AllocationGroupShift(); // check if the device size is large enough to hold the file system - status_t status = UpdateDeviceSize(); - if (status != B_OK) - RETURN_ERROR(status); - - if (fDeviceSize < (NumBlocks() << BlockShift())) + off_t diskSize; + if (opener.GetSize(&diskSize, &fDeviceBlockSize) != B_OK) + RETURN_ERROR(B_ERROR); + if (diskSize < (NumBlocks() << BlockShift())) RETURN_ERROR(B_BAD_VALUE); // set the current log pointers, so that journaling will work correctly @@ -346,7 +379,7 @@ Volume::Mount(const char* deviceName, uint32 flags) if (fJournal == NULL) return B_NO_MEMORY; - status = fJournal->InitCheck(); + status_t status = fJournal->InitCheck(); if (status < B_OK) { FATAL(("could not initialize journal: %s!\n", strerror(status))); return status; @@ -441,34 +474,6 @@ Volume::Sync() } -/*! Reads the size and block size of the device in bytes. It uses - B_GET_GEOMETRY to compute the size, or fstat() if that failed. -*/ -status_t -Volume::UpdateDeviceSize() -{ - device_geometry geometry; - if (ioctl(fDevice, B_GET_GEOMETRY, &geometry) < 0) { - // maybe it's just a file - struct stat stat; - if (fstat(fDevice, &stat) < 0) - return B_ERROR; - - fDeviceSize = stat.st_size; - fDeviceBlockSize = 512; - // that shouldn't cause us any problems - - return B_OK; - } - - fDeviceSize = 1LL * geometry.head_count * geometry.cylinder_count - * geometry.sectors_per_track * geometry.bytes_per_sector; - fDeviceBlockSize = geometry.bytes_per_sector; - - return B_OK; -} - - status_t Volume::ValidateBlockRun(block_run run) { @@ -783,11 +788,12 @@ Volume::Initialize(int fd, const char* name, uint32 blockSize, fDevice = opener.Device(); - status_t status = UpdateDeviceSize(); - if (status != B_OK) - return status; + uint32 deviceBlockSize; + off_t deviceSize; + if (opener.GetSize(&deviceSize, &deviceBlockSize) < B_OK) + return B_ERROR; - off_t numBlocks = fDeviceSize / blockSize; + off_t numBlocks = deviceSize / blockSize; // create valid super block @@ -798,7 +804,7 @@ Volume::Initialize(int fd, const char* name, uint32 blockSize, fBlockShift = fSuperBlock.BlockShift(); fAllocationGroupShift = fSuperBlock.AllocationGroupShift(); - off_t logSize = CalculateLogSize(numBlocks, fDeviceSize); + off_t logSize = CalculateLogSize(numBlocks, deviceSize); // since the allocator has not been initialized yet, we // cannot use BlockAllocator::BitmapSize() here @@ -831,7 +837,7 @@ Volume::Initialize(int fd, const char* name, uint32 blockSize, RETURN_ERROR(B_ERROR); off_t id; - status = Inode::Create(transaction, NULL, NULL, + status_t status = Inode::Create(transaction, NULL, NULL, S_DIRECTORY | 0755, 0, 0, NULL, &id, &fRootNode); if (status < B_OK) RETURN_ERROR(status); diff --git a/src/add-ons/kernel/file_systems/bfs/Volume.h b/src/add-ons/kernel/file_systems/bfs/Volume.h index e22d4b7..40ae6bf 100644 --- a/src/add-ons/kernel/file_systems/bfs/Volume.h +++ b/src/add-ons/kernel/file_systems/bfs/Volume.h @@ -73,10 +73,7 @@ public: { return (NumBlocks() + fBlockSize * 8 - 1) / (fBlockSize * 8); } - status_t UpdateDeviceSize(); - off_t DeviceSize() const { return fDeviceSize; } uint32 DeviceBlockSize() const { return fDeviceBlockSize; } - uint32 BlockSize() const { return fBlockSize; } uint32 BlockShift() const { return fBlockShift; } uint32 InodeSize() const @@ -169,7 +166,6 @@ protected: int fDevice; disk_super_block fSuperBlock; - off_t fDeviceSize; uint32 fDeviceBlockSize; uint32 fBlockSize; uint32 fBlockShift; ############################################################################ Commit: e8d0bc3a5956076c0367eecc7b82ac084447d767 Author: ahenriksson <sausageboy@xxxxxxxxx> Date: Fri Aug 3 09:04:34 2012 UTC Added code to update the block cache size ---------------------------------------------------------------------------- diff --git a/src/add-ons/kernel/file_systems/bfs/Journal.cpp b/src/add-ons/kernel/file_systems/bfs/Journal.cpp index b89f45a..bacfde1 100644 --- a/src/add-ons/kernel/file_systems/bfs/Journal.cpp +++ b/src/add-ons/kernel/file_systems/bfs/Journal.cpp @@ -900,18 +900,18 @@ 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. If \a movingLog is \c + also write back all dirty blocks for this volume. If \a alreadyLocked is \c true, we allow the lock to be held when the function is called. */ status_t -Journal::_FlushLog(bool canWait, bool flushBlocks, bool movingLog) +Journal::_FlushLog(bool canWait, bool flushBlocks, bool alreadyLocked) { status_t status = canWait ? recursive_lock_lock(&fLock) : recursive_lock_trylock(&fLock); if (status != B_OK) return status; - int32 allowedLocks = movingLog ? 2 : 1; + int32 allowedLocks = alreadyLocked ? 2 : 1; if (recursive_lock_get_recursion(&fLock) > allowedLocks) { // whoa, FlushLogAndBlocks() was called from inside a transaction recursive_lock_unlock(&fLock); @@ -944,6 +944,26 @@ Journal::FlushLogAndBlocks() } +/*! Locks the journal, in addition to flushing the log and blocks. A return + value of \c B_OK indicates that the operation was successful, and that + the journal is locked. +*/ +status_t +Journal::FlushLogAndLockJournal() +{ + status_t status = Lock(NULL, true); + if (status != B_OK) + return status; + + status = _FlushLog(true, true, true); + + if (status != B_OK) + recursive_lock_unlock(&fLock); + + return status; +} + + status_t Journal::Lock(Transaction* owner, bool separateSubTransactions) { @@ -1122,19 +1142,19 @@ Journal::MoveLog(block_run newLog) return status; } - RecursiveLocker locker(fLock); + MutexLocker volumeLock(fVolume->Lock()); - status = _FlushLog(true, true, true); + status = FlushLogAndLockJournal(); 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; + + Unlock(NULL, true); // if we had to allocate some blocks, try to free them if (!allocatedRun.IsZero()) { @@ -1154,8 +1174,8 @@ Journal::MoveLog(block_run newLog) fLogSize = newLog.Length(); fMaxTransactionSize = fLogSize / 2 - 5; + Unlock(NULL, true); volumeLock.Unlock(); - locker.Unlock(); // at this point, the log is moved and functional in its new location diff --git a/src/add-ons/kernel/file_systems/bfs/Journal.h b/src/add-ons/kernel/file_systems/bfs/Journal.h index a168cd8..5a519a6 100644 --- a/src/add-ons/kernel/file_systems/bfs/Journal.h +++ b/src/add-ons/kernel/file_systems/bfs/Journal.h @@ -36,6 +36,8 @@ public: bool CurrentTransactionTooLarge() const; status_t FlushLogAndBlocks(); + status_t FlushLogAndLockJournal(); + Volume* GetVolume() const { return fVolume; } int32 TransactionID() const { return fTransactionID; } @@ -52,7 +54,7 @@ private: { return fHasSubtransaction; } status_t _FlushLog(bool canWait, bool flushBlocks, - bool movingLog = false); + bool alreadyLocked = false); uint32 _TransactionSize() const; status_t _WriteTransactionToLog(); status_t _CheckRunArray(const run_array* array); diff --git a/src/add-ons/kernel/file_systems/bfs/ResizeVisitor.cpp b/src/add-ons/kernel/file_systems/bfs/ResizeVisitor.cpp index eef632e..b3ec05f 100644 --- a/src/add-ons/kernel/file_systems/bfs/ResizeVisitor.cpp +++ b/src/add-ons/kernel/file_systems/bfs/ResizeVisitor.cpp @@ -307,6 +307,11 @@ ResizeVisitor::_ResizeVolume() if (status != B_OK) return status; + // change size of block cache + status = GetVolume()->ResizeBlockCache(fNumBlocks); + if (status != B_OK) + return status; + // update superblock and volume information disk_super_block& superBlock = GetVolume()->SuperBlock(); diff --git a/src/add-ons/kernel/file_systems/bfs/Volume.cpp b/src/add-ons/kernel/file_systems/bfs/Volume.cpp index ae2fce5..7021b27 100644 --- a/src/add-ons/kernel/file_systems/bfs/Volume.cpp +++ b/src/add-ons/kernel/file_systems/bfs/Volume.cpp @@ -475,6 +475,31 @@ Volume::Sync() status_t +Volume::ResizeBlockCache(off_t numBlocks) +{ + status_t status = fJournal->FlushLogAndLockJournal(); + if (status != B_OK) + return status; + + void* newBlockCache = block_cache_create(fDevice, numBlocks, fBlockSize, + IsReadOnly()); + + if (newBlockCache != NULL) { + block_cache_delete(fBlockCache, false); + // we have the journal flushed and locked, so there can't be + // any unwritten blocks in this cache + + fBlockCache = newBlockCache; + status = B_OK; + } else + status = B_ERROR; + + fJournal->Unlock(NULL, true); + return status; +} + + +status_t Volume::ValidateBlockRun(block_run run) { if (run.AllocationGroup() < 0 diff --git a/src/add-ons/kernel/file_systems/bfs/Volume.h b/src/add-ons/kernel/file_systems/bfs/Volume.h index 40ae6bf..faec36a 100644 --- a/src/add-ons/kernel/file_systems/bfs/Volume.h +++ b/src/add-ons/kernel/file_systems/bfs/Volume.h @@ -155,6 +155,7 @@ public: Journal* GetJournal(off_t refBlock) const; void* BlockCache() { return fBlockCache; } + status_t ResizeBlockCache(off_t numBlocks); static status_t CheckSuperBlock(const uint8* data, uint32* _offset = NULL); ############################################################################ Commit: 5cbffd392ebc19ddcecc1ccc64fe20a1c36e370f Author: ahenriksson <sausageboy@xxxxxxxxx> Date: Mon Aug 6 18:51:15 2012 UTC Fix deadlock when reinitializing block allocator ---------------------------------------------------------------------------- diff --git a/src/add-ons/kernel/file_systems/bfs/BlockAllocator.cpp b/src/add-ons/kernel/file_systems/bfs/BlockAllocator.cpp index e310ddf..59274c2 100644 --- a/src/add-ons/kernel/file_systems/bfs/BlockAllocator.cpp +++ b/src/add-ons/kernel/file_systems/bfs/BlockAllocator.cpp @@ -613,16 +613,19 @@ BlockAllocator::InitializeAndClearBitmap(Transaction& transaction) status_t BlockAllocator::Reinitialize() { - recursive_lock_lock(&fLock); - // the lock will be unlocked in the call to Initialize() - // need to write back any pending changes to the block bitmap // TODO: shall we read through the cache in _Initialize instead? - status_t status = fVolume->GetJournal(0)->FlushLogAndBlocks(); - if (status != B_OK) { - recursive_lock_unlock(&fLock); + status_t status = fVolume->GetJournal(0)->FlushLogAndLockJournal(); + if (status != B_OK) return status; - } + + recursive_lock_lock(&fLock); + // the lock will be unlocked in the call to Initialize() + + fVolume->GetJournal(0)->Unlock(NULL, true); + // unlock the journal here: we only need to make sure that no one + // starts a transaction before we get the allocator lock, as that + // would cause a deadlock delete[] fGroups; return Initialize(true, true);