* Replaced the specialized inode in transaction mechanism with a generic one.

That costs 12 more bytes per inode on 32 bit platforms, though.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@35491 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Axel Dörfler 2010-02-16 11:30:15 +00:00
parent 2a8ae7fb92
commit fd91cf4d18
7 changed files with 322 additions and 261 deletions

View File

@ -3,6 +3,7 @@
* This file may be used under the terms of the MIT License. * This file may be used under the terms of the MIT License.
*/ */
//! block bitmap handling and allocation policies //! block bitmap handling and allocation policies

View File

@ -3,7 +3,8 @@
* This file may be used under the terms of the MIT License. * This file may be used under the terms of the MIT License.
*/ */
//! inode access functions
//! Inode access functions
#include "Debug.h" #include "Debug.h"
@ -175,7 +176,7 @@ InodeAllocator::~InodeAllocator()
fInode->Node().flags &= ~HOST_ENDIAN_TO_BFS_INT32(INODE_IN_USE); fInode->Node().flags &= ~HOST_ENDIAN_TO_BFS_INT32(INODE_IN_USE);
// this unblocks any pending bfs_read_vnode() calls // this unblocks any pending bfs_read_vnode() calls
fInode->Free(*fTransaction); fInode->Free(*fTransaction);
fTransaction->RemoveInode(fInode); fTransaction->RemoveListener(fInode);
remove_vnode(volume->FSVolume(), fInode->ID()); remove_vnode(volume->FSVolume(), fInode->ID());
} else } else
@ -291,7 +292,7 @@ InodeAllocator::_TransactionListener(int32 id, int32 event, void* _inode)
status_t status_t
bfs_inode::InitCheck(Volume* volume) bfs_inode::InitCheck(Volume* volume) const
{ {
if (Magic1() != INODE_MAGIC1 if (Magic1() != INODE_MAGIC1
|| !(Flags() & INODE_IN_USE) || !(Flags() & INODE_IN_USE)
@ -403,7 +404,7 @@ Inode::~Inode()
status_t status_t
Inode::InitCheck(bool checkNode) Inode::InitCheck(bool checkNode) const
{ {
// test inode magic and flags // test inode magic and flags
if (checkNode) { if (checkNode) {
@ -437,6 +438,33 @@ Inode::InitCheck(bool checkNode)
} }
/*! Adds this inode to the specified transaction. This means that the inode will
be write locked until the transaction ended.
To ensure that the inode will stay valid until that point, an extra reference
is acquired to it as long as this transaction stays active.
*/
void
Inode::WriteLockInTransaction(Transaction& transaction)
{
// These flags can only change while holding the transaction lock
if ((Flags() & INODE_IN_TRANSACTION) != 0)
return;
// We share the same list link with the removed list, so we have to remove
// the inode from that list here (and add it back when we no longer need it)
if ((Flags() & INODE_DELETED) != 0)
fVolume->RemovedInodes().Remove(this);
if (!fVolume->IsInitializing())
acquire_vnode(fVolume->FSVolume(), ID());
rw_lock_write_lock(&Lock());
Node().flags |= HOST_ENDIAN_TO_BFS_INT32(INODE_IN_TRANSACTION);
transaction.AddListener(this);
}
status_t status_t
Inode::WriteBack(Transaction& transaction) Inode::WriteBack(Transaction& transaction)
{ {
@ -2379,6 +2407,35 @@ Inode::Sync()
} }
// #pragma mark - TransactionListener implementation
void
Inode::TransactionDone(bool success)
{
if (!success) {
// revert any changes made to the cached bfs_inode
UpdateNodeFromDisk();
}
}
void
Inode::RemovedFromTransaction()
{
Node().flags &= ~HOST_ENDIAN_TO_BFS_INT32(INODE_IN_TRANSACTION);
// See AddInode() why we do this here
if ((Flags() & INODE_DELETED) != 0)
fVolume->RemovedInodes().Add(this);
rw_lock_write_unlock(&Lock());
if (!fVolume->IsInitializing())
put_vnode(fVolume->FSVolume(), ID());
}
// #pragma mark - creation/deletion // #pragma mark - creation/deletion

View File

@ -23,7 +23,7 @@ class NodeGetter;
class Transaction; class Transaction;
class Inode { class Inode : public TransactionListener {
typedef DoublyLinkedListLink<Inode> Link; typedef DoublyLinkedListLink<Inode> Link;
public: public:
@ -32,14 +32,15 @@ public:
ino_t id, mode_t mode, block_run& run); ino_t id, mode_t mode, block_run& run);
~Inode(); ~Inode();
status_t InitCheck(bool checkNode = true) const;
ino_t ID() const { return fID; } ino_t ID() const { return fID; }
off_t BlockNumber() const off_t BlockNumber() const
{ return fVolume->VnodeToBlock(fID); } { return fVolume->VnodeToBlock(fID); }
rw_lock& Lock() { return fLock; } rw_lock& Lock() { return fLock; }
ReadLocker ReadLock() { return ReadLocker(fLock); } ReadLocker ReadLock() { return ReadLocker(fLock); }
void WriteLockInTransaction(Transaction& transaction) void WriteLockInTransaction(Transaction& transaction);
{ transaction.AddInode(this); }
recursive_lock& SmallDataLock() { return fSmallDataLock; } recursive_lock& SmallDataLock() { return fSmallDataLock; }
@ -60,17 +61,19 @@ public:
{ return (Mode() & S_EXTENDED_TYPES) { return (Mode() & S_EXTENDED_TYPES)
== S_ATTR; } == S_ATTR; }
bool IsFile() const bool IsFile() const
{ return (Mode() { return (Mode() & (S_IFMT
& (S_IFMT | S_EXTENDED_TYPES)) == S_FILE; } | S_EXTENDED_TYPES)) == S_FILE; }
bool IsRegularNode() const bool IsRegularNode() const
{ return (Mode() & S_EXTENDED_TYPES) == 0; } { return (Mode() & S_EXTENDED_TYPES) == 0; }
// a regular node in the standard namespace // a regular node in the standard namespace
// (i.e. not an index or attribute) // (i.e. not an index or attribute)
bool IsSymLink() const { return S_ISLNK(Mode()); } bool IsSymLink() const { return S_ISLNK(Mode()); }
bool IsLongSymLink() const bool IsLongSymLink() const
{ return (Flags() & INODE_LONG_SYMLINK) != 0; } { return (Flags() & INODE_LONG_SYMLINK)
!= 0; }
bool HasUserAccessableStream() const { return IsFile(); } bool HasUserAccessableStream() const
{ return IsFile(); }
// currently only files can be accessed with // currently only files can be accessed with
// bfs_read()/bfs_write() // bfs_read()/bfs_write()
bool NeedsFileCache() const bool NeedsFileCache() const
@ -96,17 +99,16 @@ public:
Volume* GetVolume() const { return fVolume; } Volume* GetVolume() const { return fVolume; }
status_t InitCheck(bool checkNode = true);
status_t CheckPermissions(int accessMode) const; status_t CheckPermissions(int accessMode) const;
// small_data access methods // small_data access methods
small_data* FindSmallData(const bfs_inode* node, small_data* FindSmallData(const bfs_inode* node,
const char* name) const; const char* name) const;
const char* Name(const bfs_inode* node) const; const char* Name(const bfs_inode* node) const;
status_t GetName(char* buffer, status_t GetName(char* buffer, size_t bufferSize
size_t bufferSize = B_FILE_NAME_LENGTH) const; = B_FILE_NAME_LENGTH) const;
status_t SetName(Transaction& transaction, const char* name); status_t SetName(Transaction& transaction,
const char* name);
// high-level attribute methods // high-level attribute methods
status_t ReadAttribute(const char* name, int32 type, status_t ReadAttribute(const char* name, int32 type,
@ -119,7 +121,8 @@ public:
const char* name); const char* name);
// attribute methods // attribute methods
status_t GetAttribute(const char* name, Inode** attribute); status_t GetAttribute(const char* name,
Inode** _attribute);
void ReleaseAttribute(Inode* attribute); void ReleaseAttribute(Inode* attribute);
status_t CreateAttribute(Transaction& transaction, status_t CreateAttribute(Transaction& transaction,
const char* name, uint32 type, const char* name, uint32 type,
@ -128,7 +131,8 @@ public:
// for directories only: // for directories only:
BPlusTree* Tree() const { return fTree; } BPlusTree* Tree() const { return fTree; }
bool IsEmpty(); bool IsEmpty();
status_t ContainerContentsChanged(Transaction& transaction); status_t ContainerContentsChanged(
Transaction& transaction);
// manipulating the data stream // manipulating the data stream
status_t FindBlockRun(off_t pos, block_run& run, status_t FindBlockRun(off_t pos, block_run& run,
@ -139,7 +143,8 @@ public:
const uint8* buffer, size_t* length); const uint8* buffer, size_t* length);
status_t FillGapWithZeros(off_t oldSize, off_t newSize); status_t FillGapWithZeros(off_t oldSize, off_t newSize);
status_t SetFileSize(Transaction& transaction, off_t size); status_t SetFileSize(Transaction& transaction,
off_t size);
status_t Append(Transaction& transaction, off_t bytes); status_t Append(Transaction& transaction, off_t bytes);
status_t TrimPreallocation(Transaction& transaction); status_t TrimPreallocation(Transaction& transaction);
bool NeedsTrimming() const; bool NeedsTrimming() const;
@ -151,8 +156,9 @@ public:
const bfs_inode& Node() const { return fNode; } const bfs_inode& Node() const { return fNode; }
// create/remove inodes // create/remove inodes
status_t Remove(Transaction& transaction, const char* name, status_t Remove(Transaction& transaction,
ino_t* _id = NULL, bool isDirectory = false, const char* name, ino_t* _id = NULL,
bool isDirectory = false,
bool force = false); bool force = false);
static status_t Create(Transaction& transaction, Inode* parent, static status_t Create(Transaction& transaction, Inode* parent,
const char* name, int32 mode, int openMode, const char* name, int32 mode, int openMode,
@ -186,17 +192,16 @@ public:
{ ASSERT_WRITE_LOCKED_RW_LOCK(&fLock); } { ASSERT_WRITE_LOCKED_RW_LOCK(&fLock); }
#endif #endif
#ifdef B_HAIKU_64_BIT
Link* GetDoublyLinkedListLink() Link* GetDoublyLinkedListLink()
{ return &fListLink; } { return (Link*)TransactionListener
::GetDoublyLinkedListLink(); }
const Link* GetDoublyLinkedListLink() const const Link* GetDoublyLinkedListLink() const
{ return &fListLink; } { return (Link*)TransactionListener
#else ::GetDoublyLinkedListLink(); }
Link* GetDoublyLinkedListLink()
{ return (Link*)&fNode.pad[0]; } protected:
const Link* GetDoublyLinkedListLink() const virtual void TransactionDone(bool success);
{ return (Link*)&fNode.pad[0]; } virtual void RemovedFromTransaction();
#endif
private: private:
Inode(const Inode& other); Inode(const Inode& other);
@ -213,15 +218,16 @@ private:
status_t _RemoveSmallData(Transaction& transaction, status_t _RemoveSmallData(Transaction& transaction,
NodeGetter& node, const char* name); NodeGetter& node, const char* name);
status_t _AddSmallData(Transaction& transaction, status_t _AddSmallData(Transaction& transaction,
NodeGetter& node, const char* name, uint32 type, NodeGetter& node, const char* name,
off_t pos, const uint8* data, size_t length, uint32 type, off_t pos, const uint8* data,
bool force = false); size_t length, bool force = false);
status_t _GetNextSmallData(bfs_inode* node, status_t _GetNextSmallData(bfs_inode* node,
small_data** _smallData) const; small_data** _smallData) const;
status_t _RemoveSmallData(bfs_inode* node, small_data* item, status_t _RemoveSmallData(bfs_inode* node,
int32 index); small_data* item, int32 index);
status_t _RemoveAttribute(Transaction& transaction, status_t _RemoveAttribute(Transaction& transaction,
const char* name, bool hasIndex, Index* index); const char* name, bool hasIndex,
Index* index);
void _AddIterator(AttributeIterator* iterator); void _AddIterator(AttributeIterator* iterator);
void _RemoveIterator(AttributeIterator* iterator); void _RemoveIterator(AttributeIterator* iterator);
@ -236,8 +242,10 @@ private:
status_t _AllocateBlockArray(Transaction& transaction, status_t _AllocateBlockArray(Transaction& transaction,
block_run& run, size_t length, block_run& run, size_t length,
bool variableSize = false); bool variableSize = false);
status_t _GrowStream(Transaction& transaction, off_t size); status_t _GrowStream(Transaction& transaction,
status_t _ShrinkStream(Transaction& transaction, off_t size); off_t size);
status_t _ShrinkStream(Transaction& transaction,
off_t size);
private: private:
rw_lock fLock; rw_lock fLock;
@ -254,10 +262,6 @@ private:
// we need those values to ensure we will remove // we need those values to ensure we will remove
// the correct keys from the indices // the correct keys from the indices
#ifdef B_HAIKU_64_BIT
Link fListLink;
#endif
mutable recursive_lock fSmallDataLock; mutable recursive_lock fSmallDataLock;
SinglyLinkedList<AttributeIterator> fIterators; SinglyLinkedList<AttributeIterator> fIterators;
}; };

View File

@ -1,8 +1,9 @@
/* /*
* Copyright 2001-2009, Axel Dörfler, axeld@pinc-software.de. * Copyright 2001-2010, 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.
*/ */
//! Transaction and logging //! Transaction and logging
@ -1005,7 +1006,7 @@ Journal::Unlock(Transaction* owner, bool success)
// closed. // closed.
bool separateSubTransactions = fSeparateSubTransactions; bool separateSubTransactions = fSeparateSubTransactions;
fSeparateSubTransactions = true; fSeparateSubTransactions = true;
owner->UnlockInodes(success); owner->NotifyListeners(success);
fSeparateSubTransactions = separateSubTransactions; fSeparateSubTransactions = separateSubTransactions;
fOwner = owner->Parent(); fOwner = owner->Parent();
@ -1018,7 +1019,7 @@ Journal::Unlock(Transaction* owner, bool success)
&& recursive_lock_get_recursion(&fLock) == 1) && recursive_lock_get_recursion(&fLock) == 1)
fSeparateSubTransactions = false; fSeparateSubTransactions = false;
} else } else
owner->MoveInodesTo(fOwner); owner->MoveListenersTo(fOwner);
recursive_lock_unlock(&fLock); recursive_lock_unlock(&fLock);
return B_OK; return B_OK;
@ -1077,6 +1078,7 @@ Journal::_TransactionDone(bool success)
#ifdef BFS_DEBUGGER_COMMANDS #ifdef BFS_DEBUGGER_COMMANDS
void void
Journal::Dump() Journal::Dump()
{ {
@ -1121,9 +1123,23 @@ dump_journal(int argc, char** argv)
return 0; return 0;
} }
#endif // BFS_DEBUGGER_COMMANDS #endif // BFS_DEBUGGER_COMMANDS
// #pragma mark - TransactionListener
TransactionListener::TransactionListener()
{
}
TransactionListener::~TransactionListener()
{
}
// #pragma mark - Transaction // #pragma mark - Transaction
@ -1143,74 +1159,33 @@ Transaction::Start(Volume* volume, off_t refBlock)
} }
/*! Adds an inode to this transaction. This means that the inode will be write
locked until the transaction ended.
To ensure that the inode will stay valid until that point, an extra reference
is acquired to it as long as this transaction stays active.
*/
void void
Transaction::AddInode(Inode* inode) Transaction::AddListener(TransactionListener* listener)
{ {
if (fJournal == NULL) if (fJournal == NULL)
panic("Transaction is not running!"); panic("Transaction is not running!");
// These flags can only change while holding the transaction lock fListeners.Add(listener);
if ((inode->Flags() & INODE_IN_TRANSACTION) != 0)
return;
// We share the same list link with the removed list, so we have to remove
// the inode from that list here (and add it back when we no longer need it)
if ((inode->Flags() & INODE_DELETED) != 0)
GetVolume()->RemovedInodes().Remove(inode);
if (!GetVolume()->IsInitializing())
acquire_vnode(GetVolume()->FSVolume(), inode->ID());
rw_lock_write_lock(&inode->Lock());
fLockedInodes.Add(inode);
inode->Node().flags |= HOST_ENDIAN_TO_BFS_INT32(INODE_IN_TRANSACTION);
} }
void void
Transaction::RemoveInode(Inode* inode) Transaction::RemoveListener(TransactionListener* listener)
{ {
if (fJournal == NULL) if (fJournal == NULL)
panic("Transaction is not running!"); panic("Transaction is not running!");
inode->Node().flags &= ~HOST_ENDIAN_TO_BFS_INT32(INODE_IN_TRANSACTION); fListeners.Remove(listener);
fLockedInodes.Remove(inode); listener->RemovedFromTransaction();
rw_lock_write_unlock(&inode->Lock());
// See AddInode() why we do this here
if ((inode->Flags() & INODE_DELETED) != 0)
GetVolume()->RemovedInodes().Add(inode);
if (!GetVolume()->IsInitializing())
put_vnode(GetVolume()->FSVolume(), inode->ID());
} }
void void
Transaction::UnlockInodes(bool success) Transaction::NotifyListeners(bool success)
{ {
while (Inode* inode = fLockedInodes.RemoveHead()) { while (TransactionListener* listener = fListeners.RemoveHead()) {
if (success) { listener->TransactionDone(success);
inode->Node().flags listener->RemovedFromTransaction();
&= ~HOST_ENDIAN_TO_BFS_INT32(INODE_IN_TRANSACTION);
} else {
// revert any changes made to the cached bfs_inode
inode->UpdateNodeFromDisk();
}
rw_lock_write_unlock(&inode->Lock());
// See AddInode() why we do this here
if ((inode->Flags() & INODE_DELETED) != 0)
GetVolume()->RemovedInodes().Add(inode);
if (!GetVolume()->IsInitializing())
put_vnode(GetVolume()->FSVolume(), inode->ID());
} }
} }
@ -1219,9 +1194,9 @@ Transaction::UnlockInodes(bool success)
sure they will still be reverted in case the transaction is aborted. sure they will still be reverted in case the transaction is aborted.
*/ */
void void
Transaction::MoveInodesTo(Transaction* transaction) Transaction::MoveListenersTo(Transaction* transaction)
{ {
while (Inode* inode = fLockedInodes.RemoveHead()) { while (TransactionListener* listener = fListeners.RemoveHead()) {
transaction->fLockedInodes.Add(inode); transaction->fListeners.Add(listener);
} }
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2001-2009, Axel Dörfler, axeld@pinc-software.de. * Copyright 2001-2010, 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.
*/ */
#ifndef JOURNAL_H #ifndef JOURNAL_H
@ -86,6 +86,19 @@ Journal::FreeLogBlocks() const
} }
class TransactionListener
: public DoublyLinkedListLinkImpl<TransactionListener> {
public:
TransactionListener();
virtual ~TransactionListener();
virtual void TransactionDone(bool success) = 0;
virtual void RemovedFromTransaction() = 0;
};
typedef DoublyLinkedList<TransactionListener> TransactionListeners;
class Transaction { class Transaction {
public: public:
Transaction(Volume* volume, off_t refBlock) Transaction(Volume* volume, off_t refBlock)
@ -176,11 +189,11 @@ public:
int32 ID() const int32 ID() const
{ return fJournal->TransactionID(); } { return fJournal->TransactionID(); }
void AddInode(Inode* inode); void AddListener(TransactionListener* listener);
void RemoveInode(Inode* inode); void RemoveListener(TransactionListener* listener);
void UnlockInodes(bool success); void NotifyListeners(bool success);
void MoveInodesTo(Transaction* transaction); void MoveListenersTo(Transaction* transaction);
void SetParent(Transaction* parent) void SetParent(Transaction* parent)
{ fParent = parent; } { fParent = parent; }
@ -193,12 +206,14 @@ private:
// no implementation // no implementation
Journal* fJournal; Journal* fJournal;
InodeList fLockedInodes; TransactionListeners fListeners;
Transaction* fParent; Transaction* fParent;
}; };
#ifdef BFS_DEBUGGER_COMMANDS #ifdef BFS_DEBUGGER_COMMANDS
int dump_journal(int argc, char** argv); int dump_journal(int argc, char** argv);
#endif #endif
#endif // JOURNAL_H #endif // JOURNAL_H

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2001-2009, Axel Dörfler, axeld@pinc-software.de. * Copyright 2001-2010, 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.
*/ */
#ifndef VOLUME_H #ifndef VOLUME_H
@ -11,10 +11,12 @@
#include "bfs.h" #include "bfs.h"
#include "BlockAllocator.h" #include "BlockAllocator.h"
class Journal; class Journal;
class Inode; class Inode;
class Query; class Query;
enum volume_flags { enum volume_flags {
VOLUME_READ_ONLY = 0x0001 VOLUME_READ_ONLY = 0x0001
}; };
@ -25,6 +27,7 @@ enum volume_initialize_flags {
typedef DoublyLinkedList<Inode> InodeList; typedef DoublyLinkedList<Inode> InodeList;
class Volume { class Volume {
public: public:
Volume(fs_volume* volume); Volume(fs_volume* volume);

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2001-2009, Axel Dörfler, axeld@pinc-software.de. * Copyright 2001-2010, Axel Dörfler, axeld@pinc-software.de.
* Parts of this code is based on work previously done by Marcus Overhagen. * Parts of this code is based on work previously done by Marcus Overhagen.
* *
* This file may be used under the terms of the MIT License. * This file may be used under the terms of the MIT License.
@ -7,6 +7,7 @@
#ifndef BFS_H #ifndef BFS_H
#define BFS_H #define BFS_H
//! BFS definitions and helper functions //! BFS definitions and helper functions
@ -149,15 +150,20 @@ struct small_data {
uint16 data_size; uint16 data_size;
char name[0]; // name_size long, followed by data char name[0]; // name_size long, followed by data
uint32 Type() const { return BFS_ENDIAN_TO_HOST_INT32(type); } uint32 Type() const
uint16 NameSize() const { return BFS_ENDIAN_TO_HOST_INT16(name_size); } { return BFS_ENDIAN_TO_HOST_INT32(type); }
uint16 DataSize() const { return BFS_ENDIAN_TO_HOST_INT16(data_size); } uint16 NameSize() const
{ return BFS_ENDIAN_TO_HOST_INT16(
name_size); }
uint16 DataSize() const
{ return BFS_ENDIAN_TO_HOST_INT16(
data_size); }
inline char *Name() const; inline char* Name() const;
inline uint8 *Data() const; inline uint8* Data() const;
inline uint32 Size() const; inline uint32 Size() const;
inline small_data *Next() const; inline small_data* Next() const;
inline bool IsLast(const bfs_inode *inode) const; inline bool IsLast(const bfs_inode* inode) const;
} _PACKED; } _PACKED;
// the file name is part of the small_data structure // the file name is part of the small_data structure
@ -222,7 +228,7 @@ struct bfs_inode {
{ return BFS_ENDIAN_TO_HOST_INT64(status_change_time); } { return BFS_ENDIAN_TO_HOST_INT64(status_change_time); }
small_data* SmallDataStart() { return small_data_start; } small_data* SmallDataStart() { return small_data_start; }
status_t InitCheck(Volume *volume); status_t InitCheck(Volume* volume) const;
// defined in Inode.cpp // defined in Inode.cpp
static int64 ToInode(bigtime_t time) static int64 ToInode(bigtime_t time)