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:
@@ -20,7 +20,7 @@ Block::TransferFrom(Block& other)
|
|||||||
fVolume = other.fVolume;
|
fVolume = other.fVolume;
|
||||||
fData = other.fData;
|
fData = other.fData;
|
||||||
fIndex = other.fIndex;
|
fIndex = other.fIndex;
|
||||||
fWritable = other.fWritable;
|
fTransaction = other.fTransaction;
|
||||||
|
|
||||||
other.fVolume = NULL;
|
other.fVolume = NULL;
|
||||||
other.fData = NULL;
|
other.fData = NULL;
|
||||||
@@ -33,7 +33,7 @@ Block::GetReadable(Volume* volume, uint64 blockIndex)
|
|||||||
Put();
|
Put();
|
||||||
|
|
||||||
return _Init(volume, blockIndex,
|
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();
|
Put();
|
||||||
|
|
||||||
|
status_t error = transaction.RegisterBlock(blockIndex);
|
||||||
|
if (error != B_OK)
|
||||||
|
return error;
|
||||||
|
|
||||||
return _Init(volume, blockIndex,
|
return _Init(volume, blockIndex,
|
||||||
block_cache_get_writable(volume->BlockCache(), blockIndex,
|
block_cache_get_writable(volume->BlockCache(), blockIndex,
|
||||||
transaction.ID()),
|
transaction.ID()),
|
||||||
true);
|
&transaction);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -54,10 +58,14 @@ Block::GetZero(Volume* volume, uint64 blockIndex, Transaction& transaction)
|
|||||||
{
|
{
|
||||||
Put();
|
Put();
|
||||||
|
|
||||||
|
status_t error = transaction.RegisterBlock(blockIndex);
|
||||||
|
if (error != B_OK)
|
||||||
|
return error;
|
||||||
|
|
||||||
return _Init(volume, blockIndex,
|
return _Init(volume, blockIndex,
|
||||||
block_cache_get_empty(volume->BlockCache(), blockIndex,
|
block_cache_get_empty(volume->BlockCache(), blockIndex,
|
||||||
transaction.ID()),
|
transaction.ID()),
|
||||||
true);
|
&transaction);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -66,15 +74,21 @@ Block::MakeWritable(Transaction& transaction)
|
|||||||
{
|
{
|
||||||
if (fVolume == NULL)
|
if (fVolume == NULL)
|
||||||
return B_BAD_VALUE;
|
return B_BAD_VALUE;
|
||||||
if (fWritable)
|
if (fTransaction != NULL)
|
||||||
return B_OK;
|
return B_OK;
|
||||||
|
|
||||||
status_t error = block_cache_make_writable(fVolume->BlockCache(),
|
status_t error = transaction.RegisterBlock(fIndex);
|
||||||
fIndex, transaction.ID());
|
|
||||||
if (error != B_OK)
|
if (error != B_OK)
|
||||||
return error;
|
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;
|
return B_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,6 +97,9 @@ void
|
|||||||
Block::Put()
|
Block::Put()
|
||||||
{
|
{
|
||||||
if (fVolume != NULL) {
|
if (fVolume != NULL) {
|
||||||
|
if (fTransaction != NULL)
|
||||||
|
fTransaction->PutBlock(fIndex, fData);
|
||||||
|
|
||||||
block_cache_put(fVolume->BlockCache(), fIndex);
|
block_cache_put(fVolume->BlockCache(), fIndex);
|
||||||
fVolume = NULL;
|
fVolume = NULL;
|
||||||
fData = NULL;
|
fData = NULL;
|
||||||
@@ -91,7 +108,8 @@ Block::Put()
|
|||||||
|
|
||||||
|
|
||||||
bool
|
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)
|
if (data == NULL)
|
||||||
return false;
|
return false;
|
||||||
@@ -99,7 +117,7 @@ Block::_Init(Volume* volume, uint64 blockIndex, const void* data, bool writable)
|
|||||||
fVolume = volume;
|
fVolume = volume;
|
||||||
fData = const_cast<void*>(data);
|
fData = const_cast<void*>(data);
|
||||||
fIndex = blockIndex;
|
fIndex = blockIndex;
|
||||||
fWritable = writable;
|
fTransaction = transaction;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,13 +35,13 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
bool _Init(Volume* volume, uint64 blockIndex,
|
bool _Init(Volume* volume, uint64 blockIndex,
|
||||||
const void* data, bool writable);
|
const void* data, Transaction* transaction);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Volume* fVolume;
|
Volume* fVolume;
|
||||||
void* fData;
|
void* fData;
|
||||||
uint64 fIndex;
|
uint64 fIndex;
|
||||||
bool fWritable;
|
Transaction* fTransaction;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -6,8 +6,12 @@
|
|||||||
|
|
||||||
#include "Transaction.h"
|
#include "Transaction.h"
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
|
#include <AutoDeleter.h>
|
||||||
|
|
||||||
#include "BlockAllocator.h"
|
#include "BlockAllocator.h"
|
||||||
#include "Volume.h"
|
#include "Volume.h"
|
||||||
|
|
||||||
@@ -29,6 +33,8 @@ swap_if_greater(Node*& a, Node*& b)
|
|||||||
Transaction::Transaction(Volume* volume)
|
Transaction::Transaction(Volume* volume)
|
||||||
:
|
:
|
||||||
fVolume(volume),
|
fVolume(volume),
|
||||||
|
fSHA256(NULL),
|
||||||
|
fCheckSum(NULL),
|
||||||
fID(-1)
|
fID(-1)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@@ -37,6 +43,9 @@ Transaction::Transaction(Volume* volume)
|
|||||||
Transaction::~Transaction()
|
Transaction::~Transaction()
|
||||||
{
|
{
|
||||||
Abort();
|
Abort();
|
||||||
|
|
||||||
|
delete fCheckSum;
|
||||||
|
delete fSHA256;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -45,6 +54,22 @@ Transaction::Start()
|
|||||||
{
|
{
|
||||||
ASSERT(fID < 0);
|
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();
|
fVolume->TransactionStarted();
|
||||||
|
|
||||||
fID = cache_start_transaction(fVolume->BlockCache());
|
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
|
// commit the cache transaction
|
||||||
status_t error = cache_end_transaction(fVolume->BlockCache(), fID, NULL,
|
error = cache_end_transaction(fVolume->BlockCache(), fID, NULL, NULL);
|
||||||
NULL);
|
|
||||||
if (error != B_OK) {
|
if (error != B_OK) {
|
||||||
Abort();
|
Abort();
|
||||||
return error;
|
return error;
|
||||||
@@ -128,9 +169,24 @@ Transaction::Abort()
|
|||||||
info->node->RevertNodeData(info->oldNodeData);
|
info->node->RevertNodeData(info->oldNodeData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// revert the block check sums
|
||||||
|
_RevertBlockCheckSums();
|
||||||
|
|
||||||
// clean up
|
// clean up
|
||||||
|
|
||||||
|
// delete the node infos
|
||||||
_DeleteNodeInfosAndUnlock(true);
|
_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->GetBlockAllocator()->ResetFreeBlocks(fOldFreeBlockCount);
|
||||||
|
|
||||||
fVolume->TransactionFinished();
|
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::NodeInfo*
|
||||||
Transaction::_GetNodeInfo(Node* node) const
|
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
|
// #pragma mark - PostCommitNotification
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,10 @@
|
|||||||
#include <fs_cache.h>
|
#include <fs_cache.h>
|
||||||
|
|
||||||
#include <util/DoublyLinkedList.h>
|
#include <util/DoublyLinkedList.h>
|
||||||
|
#include <util/OpenHashTable.h>
|
||||||
|
|
||||||
|
#include "CheckSum.h"
|
||||||
|
#include "driver/checksum_device.h"
|
||||||
#include "Node.h"
|
#include "Node.h"
|
||||||
|
|
||||||
|
|
||||||
@@ -59,6 +62,9 @@ public:
|
|||||||
bool IsNodeLocked(Node* node) const
|
bool IsNodeLocked(Node* node) const
|
||||||
{ return _GetNodeInfo(node) != NULL; }
|
{ return _GetNodeInfo(node) != NULL; }
|
||||||
|
|
||||||
|
status_t RegisterBlock(uint64 blockIndex);
|
||||||
|
void PutBlock(uint64 blockIndex, const void* data);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct NodeInfo : DoublyLinkedListLinkImpl<NodeInfo> {
|
struct NodeInfo : DoublyLinkedListLinkImpl<NodeInfo> {
|
||||||
Node* node;
|
Node* node;
|
||||||
@@ -68,16 +74,57 @@ private:
|
|||||||
|
|
||||||
typedef DoublyLinkedList<NodeInfo> NodeInfoList;
|
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:
|
private:
|
||||||
NodeInfo* _GetNodeInfo(Node* node) const;
|
NodeInfo* _GetNodeInfo(Node* node) const;
|
||||||
void _DeleteNodeInfosAndUnlock(bool failed);
|
void _DeleteNodeInfosAndUnlock(bool failed);
|
||||||
void _DeleteNodeInfoAndUnlock(NodeInfo* info,
|
void _DeleteNodeInfoAndUnlock(NodeInfo* info,
|
||||||
bool failed);
|
bool failed);
|
||||||
|
|
||||||
|
status_t _UpdateBlockCheckSums();
|
||||||
|
status_t _RevertBlockCheckSums();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Volume* fVolume;
|
Volume* fVolume;
|
||||||
|
SHA256* fSHA256;
|
||||||
|
checksum_device_ioctl_check_sum* fCheckSum;
|
||||||
int32 fID;
|
int32 fID;
|
||||||
NodeInfoList fNodeInfos;
|
NodeInfoList fNodeInfos;
|
||||||
|
BlockInfoTable fBlockInfos;
|
||||||
uint64 fOldFreeBlockCount;
|
uint64 fOldFreeBlockCount;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user