Block does now register writable blocks with Transaction. On commit
Transaction sets the check sums for the changed blocks on the underlying device, respectively reverts them on roll-back. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@37639 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
parent
8d591ecc41
commit
9bc87c767b
@ -20,7 +20,7 @@ Block::TransferFrom(Block& other)
|
||||
fVolume = other.fVolume;
|
||||
fData = other.fData;
|
||||
fIndex = other.fIndex;
|
||||
fWritable = other.fWritable;
|
||||
fTransaction = other.fTransaction;
|
||||
|
||||
other.fVolume = NULL;
|
||||
other.fData = NULL;
|
||||
@ -33,7 +33,7 @@ Block::GetReadable(Volume* volume, uint64 blockIndex)
|
||||
Put();
|
||||
|
||||
return _Init(volume, blockIndex,
|
||||
block_cache_get(volume->BlockCache(), blockIndex), false);
|
||||
block_cache_get(volume->BlockCache(), blockIndex), NULL);
|
||||
}
|
||||
|
||||
|
||||
@ -42,10 +42,14 @@ Block::GetWritable(Volume* volume, uint64 blockIndex, Transaction& transaction)
|
||||
{
|
||||
Put();
|
||||
|
||||
status_t error = transaction.RegisterBlock(blockIndex);
|
||||
if (error != B_OK)
|
||||
return error;
|
||||
|
||||
return _Init(volume, blockIndex,
|
||||
block_cache_get_writable(volume->BlockCache(), blockIndex,
|
||||
transaction.ID()),
|
||||
true);
|
||||
&transaction);
|
||||
}
|
||||
|
||||
|
||||
@ -54,10 +58,14 @@ Block::GetZero(Volume* volume, uint64 blockIndex, Transaction& transaction)
|
||||
{
|
||||
Put();
|
||||
|
||||
status_t error = transaction.RegisterBlock(blockIndex);
|
||||
if (error != B_OK)
|
||||
return error;
|
||||
|
||||
return _Init(volume, blockIndex,
|
||||
block_cache_get_empty(volume->BlockCache(), blockIndex,
|
||||
transaction.ID()),
|
||||
true);
|
||||
&transaction);
|
||||
}
|
||||
|
||||
|
||||
@ -66,15 +74,21 @@ Block::MakeWritable(Transaction& transaction)
|
||||
{
|
||||
if (fVolume == NULL)
|
||||
return B_BAD_VALUE;
|
||||
if (fWritable)
|
||||
if (fTransaction != NULL)
|
||||
return B_OK;
|
||||
|
||||
status_t error = block_cache_make_writable(fVolume->BlockCache(),
|
||||
fIndex, transaction.ID());
|
||||
status_t error = transaction.RegisterBlock(fIndex);
|
||||
if (error != B_OK)
|
||||
return error;
|
||||
|
||||
fWritable = true;
|
||||
error = block_cache_make_writable(fVolume->BlockCache(), fIndex,
|
||||
transaction.ID());
|
||||
if (error != B_OK) {
|
||||
transaction.PutBlock(fIndex, NULL);
|
||||
return error;
|
||||
}
|
||||
|
||||
fTransaction = &transaction;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
@ -83,6 +97,9 @@ void
|
||||
Block::Put()
|
||||
{
|
||||
if (fVolume != NULL) {
|
||||
if (fTransaction != NULL)
|
||||
fTransaction->PutBlock(fIndex, fData);
|
||||
|
||||
block_cache_put(fVolume->BlockCache(), fIndex);
|
||||
fVolume = NULL;
|
||||
fData = NULL;
|
||||
@ -91,7 +108,8 @@ Block::Put()
|
||||
|
||||
|
||||
bool
|
||||
Block::_Init(Volume* volume, uint64 blockIndex, const void* data, bool writable)
|
||||
Block::_Init(Volume* volume, uint64 blockIndex, const void* data,
|
||||
Transaction* transaction)
|
||||
{
|
||||
if (data == NULL)
|
||||
return false;
|
||||
@ -99,7 +117,7 @@ Block::_Init(Volume* volume, uint64 blockIndex, const void* data, bool writable)
|
||||
fVolume = volume;
|
||||
fData = const_cast<void*>(data);
|
||||
fIndex = blockIndex;
|
||||
fWritable = writable;
|
||||
fTransaction = transaction;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -35,13 +35,13 @@ public:
|
||||
|
||||
private:
|
||||
bool _Init(Volume* volume, uint64 blockIndex,
|
||||
const void* data, bool writable);
|
||||
const void* data, Transaction* transaction);
|
||||
|
||||
private:
|
||||
Volume* fVolume;
|
||||
void* fData;
|
||||
uint64 fIndex;
|
||||
bool fWritable;
|
||||
Transaction* fTransaction;
|
||||
};
|
||||
|
||||
|
||||
|
@ -6,8 +6,12 @@
|
||||
|
||||
#include "Transaction.h"
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include <AutoDeleter.h>
|
||||
|
||||
#include "BlockAllocator.h"
|
||||
#include "Volume.h"
|
||||
|
||||
@ -29,6 +33,8 @@ swap_if_greater(Node*& a, Node*& b)
|
||||
Transaction::Transaction(Volume* volume)
|
||||
:
|
||||
fVolume(volume),
|
||||
fSHA256(NULL),
|
||||
fCheckSum(NULL),
|
||||
fID(-1)
|
||||
{
|
||||
}
|
||||
@ -37,6 +43,9 @@ Transaction::Transaction(Volume* volume)
|
||||
Transaction::~Transaction()
|
||||
{
|
||||
Abort();
|
||||
|
||||
delete fCheckSum;
|
||||
delete fSHA256;
|
||||
}
|
||||
|
||||
|
||||
@ -45,6 +54,22 @@ Transaction::Start()
|
||||
{
|
||||
ASSERT(fID < 0);
|
||||
|
||||
status_t error = fBlockInfos.Init();
|
||||
if (error != B_OK)
|
||||
return error;
|
||||
|
||||
if (fSHA256 == NULL) {
|
||||
fSHA256 = new(std::nothrow) SHA256;
|
||||
if (fSHA256 == NULL)
|
||||
return B_NO_MEMORY;
|
||||
}
|
||||
|
||||
if (fCheckSum == NULL) {
|
||||
fCheckSum = new(std::nothrow) checksum_device_ioctl_check_sum;
|
||||
if (fCheckSum == NULL)
|
||||
return B_NO_MEMORY;
|
||||
}
|
||||
|
||||
fVolume->TransactionStarted();
|
||||
|
||||
fID = cache_start_transaction(fVolume->BlockCache());
|
||||
@ -87,9 +112,25 @@ Transaction::Commit(const PostCommitNotification* notification1,
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure the previous transaction is on disk. This is not particularly
|
||||
// performance friendly, but prevents race conditions between us setting
|
||||
// the new block check sums and the block writer deciding to write back the
|
||||
// old block data.
|
||||
status_t error = block_cache_sync(fVolume->BlockCache());
|
||||
if (error != B_OK) {
|
||||
Abort();
|
||||
return error;
|
||||
}
|
||||
|
||||
// compute the new block check sums
|
||||
error = _UpdateBlockCheckSums();
|
||||
if (error != B_OK) {
|
||||
Abort();
|
||||
return error;
|
||||
}
|
||||
|
||||
// commit the cache transaction
|
||||
status_t error = cache_end_transaction(fVolume->BlockCache(), fID, NULL,
|
||||
NULL);
|
||||
error = cache_end_transaction(fVolume->BlockCache(), fID, NULL, NULL);
|
||||
if (error != B_OK) {
|
||||
Abort();
|
||||
return error;
|
||||
@ -128,9 +169,24 @@ Transaction::Abort()
|
||||
info->node->RevertNodeData(info->oldNodeData);
|
||||
}
|
||||
|
||||
// revert the block check sums
|
||||
_RevertBlockCheckSums();
|
||||
|
||||
// clean up
|
||||
|
||||
// delete the node infos
|
||||
_DeleteNodeInfosAndUnlock(true);
|
||||
|
||||
// delete the block infos
|
||||
BlockInfo* blockInfo = fBlockInfos.Clear(true);
|
||||
while (blockInfo != NULL) {
|
||||
BlockInfo* nextInfo = blockInfo->hashNext;
|
||||
block_cache_put(fVolume->BlockCache(),
|
||||
blockInfo->indexAndCheckSum.blockIndex);
|
||||
delete nextInfo;
|
||||
blockInfo = nextInfo;
|
||||
}
|
||||
|
||||
fVolume->GetBlockAllocator()->ResetFreeBlocks(fOldFreeBlockCount);
|
||||
|
||||
fVolume->TransactionFinished();
|
||||
@ -228,6 +284,77 @@ Transaction::KeepNode(Node* node)
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
Transaction::RegisterBlock(uint64 blockIndex)
|
||||
{
|
||||
ASSERT(fID >= 0);
|
||||
|
||||
// look it up -- maybe it's already registered
|
||||
BlockInfo* info = fBlockInfos.Lookup(blockIndex);
|
||||
if (info != NULL) {
|
||||
info->refCount++;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
// nope, create a new one
|
||||
info = new(std::nothrow) BlockInfo;
|
||||
if (info == NULL)
|
||||
return B_NO_MEMORY;
|
||||
ObjectDeleter<BlockInfo> infoDeleter(info);
|
||||
|
||||
info->indexAndCheckSum.blockIndex = blockIndex;
|
||||
info->refCount = 1;
|
||||
info->dirty = false;
|
||||
|
||||
// get the old check sum
|
||||
if (ioctl(fVolume->FD(), CHECKSUM_DEVICE_IOCTL_GET_CHECK_SUM,
|
||||
&info->indexAndCheckSum, sizeof(info->indexAndCheckSum)) < 0) {
|
||||
return errno;
|
||||
}
|
||||
|
||||
// get the data (we're fine with read-only)
|
||||
info->data = block_cache_get(fVolume->BlockCache(), blockIndex);
|
||||
if (info->data == NULL) {
|
||||
delete info;
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
fBlockInfos.Insert(infoDeleter.Detach());
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Transaction::PutBlock(uint64 blockIndex, const void* data)
|
||||
{
|
||||
ASSERT(fID >= 0);
|
||||
|
||||
BlockInfo* info = fBlockInfos.Lookup(blockIndex);
|
||||
if (info == NULL) {
|
||||
panic("checksumfs: Transaction::PutBlock(): unknown block %" B_PRIu64,
|
||||
blockIndex);
|
||||
return;
|
||||
}
|
||||
|
||||
if (info->refCount == 0) {
|
||||
panic("checksumfs: Unbalanced Transaction::PutBlock(): for block %"
|
||||
B_PRIu64, blockIndex);
|
||||
return;
|
||||
}
|
||||
|
||||
info->dirty |= data != NULL;
|
||||
|
||||
if (--info->refCount == 0 && !info->dirty) {
|
||||
// block wasn't got successfully -- remove the info
|
||||
fBlockInfos.Remove(info);
|
||||
block_cache_put(fVolume->BlockCache(),
|
||||
info->indexAndCheckSum.blockIndex);
|
||||
delete info;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Transaction::NodeInfo*
|
||||
Transaction::_GetNodeInfo(Node* node) const
|
||||
{
|
||||
@ -267,6 +394,54 @@ Transaction::_DeleteNodeInfoAndUnlock(NodeInfo* info, bool failed)
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
Transaction::_UpdateBlockCheckSums()
|
||||
{
|
||||
for (BlockInfoTable::Iterator it = fBlockInfos.GetIterator();
|
||||
BlockInfo* info = it.Next();) {
|
||||
if (info->refCount > 0) {
|
||||
panic("checksumfs: Transaction::Commit(): block %" B_PRIu64
|
||||
" still referenced", info->indexAndCheckSum.blockIndex);
|
||||
}
|
||||
|
||||
if (!info->dirty)
|
||||
continue;
|
||||
|
||||
// compute the check sum
|
||||
fSHA256->Init();
|
||||
fSHA256->Update(info->data, B_PAGE_SIZE);
|
||||
fCheckSum->blockIndex = info->indexAndCheckSum.blockIndex;
|
||||
fCheckSum->checkSum = fSHA256->Digest();
|
||||
|
||||
// set it
|
||||
if (ioctl(fVolume->FD(), CHECKSUM_DEVICE_IOCTL_SET_CHECK_SUM, fCheckSum,
|
||||
sizeof(*fCheckSum)) < 0) {
|
||||
return errno;
|
||||
}
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
Transaction::_RevertBlockCheckSums()
|
||||
{
|
||||
for (BlockInfoTable::Iterator it = fBlockInfos.GetIterator();
|
||||
BlockInfo* info = it.Next();) {
|
||||
if (!info->dirty)
|
||||
continue;
|
||||
|
||||
// set the old check sum
|
||||
if (ioctl(fVolume->FD(), CHECKSUM_DEVICE_IOCTL_SET_CHECK_SUM,
|
||||
&info->indexAndCheckSum, sizeof(info->indexAndCheckSum)) < 0) {
|
||||
return errno;
|
||||
}
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark - PostCommitNotification
|
||||
|
||||
|
@ -9,7 +9,10 @@
|
||||
#include <fs_cache.h>
|
||||
|
||||
#include <util/DoublyLinkedList.h>
|
||||
#include <util/OpenHashTable.h>
|
||||
|
||||
#include "CheckSum.h"
|
||||
#include "driver/checksum_device.h"
|
||||
#include "Node.h"
|
||||
|
||||
|
||||
@ -59,6 +62,9 @@ public:
|
||||
bool IsNodeLocked(Node* node) const
|
||||
{ return _GetNodeInfo(node) != NULL; }
|
||||
|
||||
status_t RegisterBlock(uint64 blockIndex);
|
||||
void PutBlock(uint64 blockIndex, const void* data);
|
||||
|
||||
private:
|
||||
struct NodeInfo : DoublyLinkedListLinkImpl<NodeInfo> {
|
||||
Node* node;
|
||||
@ -68,16 +74,57 @@ private:
|
||||
|
||||
typedef DoublyLinkedList<NodeInfo> NodeInfoList;
|
||||
|
||||
struct BlockInfo {
|
||||
checksum_device_ioctl_check_sum indexAndCheckSum;
|
||||
BlockInfo* hashNext;
|
||||
const void* data;
|
||||
int32 refCount;
|
||||
bool dirty;
|
||||
};
|
||||
|
||||
struct BlockInfoHashDefinition {
|
||||
typedef uint64 KeyType;
|
||||
typedef BlockInfo ValueType;
|
||||
|
||||
size_t HashKey(uint64 key) const
|
||||
{
|
||||
return (size_t)key;
|
||||
}
|
||||
|
||||
size_t Hash(const BlockInfo* value) const
|
||||
{
|
||||
return HashKey(value->indexAndCheckSum.blockIndex);
|
||||
}
|
||||
|
||||
bool Compare(uint64 key, const BlockInfo* value) const
|
||||
{
|
||||
return value->indexAndCheckSum.blockIndex == key;
|
||||
}
|
||||
|
||||
BlockInfo*& GetLink(BlockInfo* value) const
|
||||
{
|
||||
return value->hashNext;
|
||||
}
|
||||
};
|
||||
|
||||
typedef BOpenHashTable<BlockInfoHashDefinition> BlockInfoTable;
|
||||
|
||||
private:
|
||||
NodeInfo* _GetNodeInfo(Node* node) const;
|
||||
void _DeleteNodeInfosAndUnlock(bool failed);
|
||||
void _DeleteNodeInfoAndUnlock(NodeInfo* info,
|
||||
bool failed);
|
||||
|
||||
status_t _UpdateBlockCheckSums();
|
||||
status_t _RevertBlockCheckSums();
|
||||
|
||||
private:
|
||||
Volume* fVolume;
|
||||
SHA256* fSHA256;
|
||||
checksum_device_ioctl_check_sum* fCheckSum;
|
||||
int32 fID;
|
||||
NodeInfoList fNodeInfos;
|
||||
BlockInfoTable fBlockInfos;
|
||||
uint64 fOldFreeBlockCount;
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user