* Added a transaction listener mechanism to be notified when a

transaction ends or has been aborted.
* BFS now listens for transactions when it created an inode to see if 
  the transaction will be aborted without freeing the inode (in which 
  case it will panic for now).
* Started implementing tracing support, but it's not working yet.
* Minor cleanup.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@23492 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Axel Dörfler 2008-01-13 17:18:29 +00:00
parent 5276dad057
commit 4fc4f2c8ae
9 changed files with 298 additions and 17 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright 2004-2007, Haiku Inc. All Rights Reserved.
* Copyright 2004-2008, Haiku Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*/
#ifndef _FS_CACHE_H
@ -11,7 +11,14 @@
#include <fs_interface.h>
typedef void (*transaction_notification_hook)(int32 id, void *data);
enum {
TRANSACTION_WRITTEN,
TRANSACTION_ABORTED,
TRANSACTION_ENDED
};
typedef void (*transaction_notification_hook)(int32 id, int32 event,
void *data);
#ifdef __cplusplus
extern "C" {
@ -27,6 +34,10 @@ extern int32 cache_detach_sub_transaction(void *_cache, int32 id,
transaction_notification_hook hook, void *data);
extern status_t cache_abort_sub_transaction(void *_cache, int32 id);
extern status_t cache_start_sub_transaction(void *_cache, int32 id);
extern status_t cache_add_transaction_listener(void *_cache, int32 id,
transaction_notification_hook hook, void *data);
extern status_t cache_remove_transaction_listener(void *_cache, int32 id,
transaction_notification_hook hook, void *data);
extern status_t cache_next_block_in_transaction(void *_cache, int32 id,
uint32 *_cookie, off_t *_blockNumber, void **_data,
void **_unchangedData);

View File

@ -1,5 +1,5 @@
/*
* Copyright 2007, Ingo Weinhold, bonefish@cs.tu-berlin.de.
* Copyright 2007-2008, Ingo Weinhold, bonefish@cs.tu-berlin.de.
* Distributed under the terms of the MIT License.
*/
#ifndef _FSSH_API_WRAPPER_H
@ -787,6 +787,10 @@
////////////////////////////////////////////////////////////////////////////////
// #pragma mark - fssh_fs_cache.h
#define TRANSACTION_WRITTEN FSSH_TRANSACTION_WRITTEN
#define TRANSACTION_ABORTED FSSH_TRANSACTION_ABORTED
#define TRANSACTION_ENDED FSSH_TRANSACTION_ENDED
#define transaction_notification_hook fssh_transaction_notification_hook
/* transactions */
@ -797,6 +801,8 @@
#define cache_detach_sub_transaction fssh_cache_detach_sub_transaction
#define cache_abort_sub_transaction fssh_cache_abort_sub_transaction
#define cache_start_sub_transaction fssh_cache_start_sub_transaction
#define cache_add_transaction_listener fssh_cache_add_transaction_listener
#define cache_remove_transaction_listener fssh_cache_remove_transaction_listener
#define cache_next_block_in_transaction fssh_cache_next_block_in_transaction
#define cache_blocks_in_transaction fssh_cache_blocks_in_transaction
#define cache_blocks_in_sub_transaction fssh_cache_blocks_in_sub_transaction

View File

@ -1,5 +1,5 @@
/*
* Copyright 2004-2007, Haiku Inc. All Rights Reserved.
* Copyright 2004-2008, Haiku Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*/
#ifndef _FSSH_FS_CACHE_H
@ -11,7 +11,14 @@
#include "fssh_fs_interface.h"
typedef void (*fssh_transaction_notification_hook)(int32_t id, void *data);
enum {
FSSH_TRANSACTION_WRITTEN,
FSSH_TRANSACTION_ABORTED,
FSSH_TRANSACTION_ENDED
};
typedef void (*fssh_transaction_notification_hook)(int32_t id, int32_t event,
void *data);
#ifdef __cplusplus
extern "C" {
@ -31,6 +38,12 @@ extern fssh_status_t fssh_cache_abort_sub_transaction(void *_cache,
int32_t id);
extern fssh_status_t fssh_cache_start_sub_transaction(void *_cache,
int32_t id);
extern fssh_status_t fssh_cache_add_transaction_listener(void *_cache,
int32_t id, fssh_transaction_notification_hook hook,
void *data);
extern fssh_status_t fssh_cache_remove_transaction_listener(void *_cache,
int32_t id, fssh_transaction_notification_hook hook,
void *data);
extern fssh_status_t fssh_cache_next_block_in_transaction(void *_cache,
int32_t id, uint32_t *_cookie,
fssh_off_t *_blockNumber, void **_data,

View File

@ -69,7 +69,7 @@ class Free : public AbstractTraceEntry {
virtual void AddDump(char *buffer, size_t size)
{
snprintf(buffer, size, "free %lu.%u.%u\n", fRun.AllocationGroup(),
snprintf(buffer, size, "free %lu.%u.%u", fRun.AllocationGroup(),
fRun.Start(), fRun.Length());
}

View File

@ -39,7 +39,7 @@ class Create : public AbstractTraceEntry {
virtual void AddDump(char *buffer, size_t size)
{
snprintf(buffer, size, "CREATE %Ld (%p), parent %Ld (%p), \"%s\", "
"mode %lx, omode %x, type %lx\n", fID, fInode, fParentID,
"mode %lx, omode %x, type %lx", fID, fInode, fParentID,
fParent, fName, fMode, fOpenMode, fType);
}
@ -92,7 +92,7 @@ class Resize : public AbstractTraceEntry {
virtual void AddDump(char *buffer, size_t size)
{
snprintf(buffer, size, "RESIZE %Ld (%p), %Ld -> %Ld\n", fID, fInode,
snprintf(buffer, size, "RESIZE %Ld (%p), %Ld -> %Ld", fID, fInode,
fOldSize, fNewSize);
}
@ -115,11 +115,14 @@ class InodeAllocator {
InodeAllocator(Transaction &transaction);
~InodeAllocator();
status_t New(block_run *parentRun, mode_t mode, block_run &run, Inode **_inode);
status_t New(block_run *parentRun, mode_t mode, block_run &run,
Inode **_inode);
status_t CreateTree();
status_t Keep();
private:
static void _TransactionListener(int32 id, int32 event, void *_inode);
Transaction *fTransaction;
block_run fRun;
Inode *fInode;
@ -224,6 +227,11 @@ InodeAllocator::Keep()
if (!fInode->IsSymLink() && volume->ID() >= 0)
status = publish_vnode(volume->ID(), fInode->ID(), fInode);
if (status == B_OK) {
cache_add_transaction_listener(volume->BlockCache(), fTransaction->ID(),
&_TransactionListener, fInode);
}
fTransaction = NULL;
fInode = NULL;
@ -231,6 +239,16 @@ InodeAllocator::Keep()
}
/*static*/ void
InodeAllocator::_TransactionListener(int32 id, int32 event, void *_inode)
{
Inode *inode = (Inode *)_inode;
if (event == TRANSACTION_ABORTED)
panic("transaction %d aborted, inode %p still around!\n", (int)id, inode);
}
// #pragma mark -

View File

@ -451,10 +451,13 @@ Journal::ReplayLog()
able to update the log start pointer.
*/
void
Journal::_blockNotify(int32 transactionID, void *arg)
Journal::_BlockNotify(int32 transactionID, int32 event, void *arg)
{
LogEntry *logEntry = (LogEntry *)arg;
if (event != TRANSACTION_WRITTEN)
return;
PRINT(("Log entry %p has been finished, transaction ID = %ld\n", logEntry, transactionID));
Journal *journal = logEntry->GetJournal();
@ -495,7 +498,7 @@ Journal::_blockNotify(int32 transactionID, void *arg)
status_t status = journal->fVolume->WriteSuperBlock();
if (status != B_OK) {
FATAL(("blockNotify: could not write back super block: %s\n",
FATAL(("_BlockNotify: could not write back super block: %s\n",
strerror(status)));
}
}
@ -656,7 +659,7 @@ Journal::_WriteTransactionToLog()
fUsed += logEntry->Length();
fEntriesLock.Unlock();
cache_end_transaction(fVolume->BlockCache(), fTransactionID, _blockNotify,
cache_end_transaction(fVolume->BlockCache(), fTransactionID, _BlockNotify,
logEntry);
// If the log goes to the next round (the log is written as a

View File

@ -59,7 +59,7 @@ class Journal {
status_t _CheckRunArray(const run_array *array);
status_t _ReplayRunArray(int32 *start);
status_t _TransactionDone(bool success);
static void _blockNotify(int32 transactionID, void *arg);
static void _BlockNotify(int32 transactionID, int32 event, void *arg);
Volume *fVolume;
RecursiveLock fLock;

View File

@ -1,5 +1,5 @@
/*
* Copyright 2004-2007, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
* Copyright 2004-2008, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
* Distributed under the terms of the MIT License.
*/
@ -12,6 +12,7 @@
#include <block_cache.h>
#include <lock.h>
#include <vm_low_memory.h>
#include <tracing.h>
#include <util/kernel_cpp.h>
#include <util/DoublyLinkedList.h>
#include <util/AutoLock.h>
@ -41,11 +42,64 @@
#define DEBUG_BLOCK_CACHE
//#define DEBUG_CHANGED
//#define TRANSACTION_TRACING
// This macro is used for fatal situations that are acceptable in a running
// system, like out of memory situations - should only panic for debugging.
#define FATAL(x) panic x
#ifdef TRANSACTION_TRACING
namespace TransactionTracing {
class Start : public AbstractTraceEntry {
public:
Start(cache_transaction *transaction)
:
fTransaction(transaction)
{
Initialized();
}
virtual void AddDump(char *buffer, size_t size)
{
}
private:
cache_transaction *fTransaction;
};
class Cancel : public AbstractTraceEntry {
public:
Cancel(cache_transaction *transaction)
:
fTransaction(transaction)
{
Initialized();
}
virtual void AddDump(char *buffer, size_t size)
{
}
private:
cache_transaction *fTransaction;
};
} // namespace TransactionTracing
# define T(x) new(std::nothrow) TransactionTracing::x;
#else
# define T(x) ;
#endif
struct cache_hook : DoublyLinkedListLinkImpl<cache_hook> {
transaction_notification_hook hook;
void *data;
};
typedef DoublyLinkedList<cache_hook> HookList;
struct cache_transaction {
cache_transaction();
@ -57,6 +111,7 @@ struct cache_transaction {
block_list blocks;
transaction_notification_hook notification_hook;
void *notification_data;
HookList listeners;
bool open;
bool has_sub_transaction;
};
@ -108,6 +163,24 @@ transaction_hash(void *_transaction, const void *_id, uint32 range)
}
/*! Notifies all listeners of this transaction, and removes them
afterwards.
*/
static void
notify_transaction_listeners(cache_transaction *transaction, int32 event)
{
HookList::Iterator iterator = transaction->listeners.GetIterator();
while (iterator.HasNext()) {
cache_hook *hook = iterator.Next();
hook->hook(transaction->id, event, hook->data);
iterator.Remove();
delete hook;
}
}
static void
delete_transaction(block_cache *cache, cache_transaction *transaction)
{
@ -712,7 +785,7 @@ write_cached_block(block_cache *cache, cached_block *block,
TRACE(("cache transaction %ld finished!\n", previous->id));
if (previous->notification_hook != NULL) {
previous->notification_hook(previous->id,
previous->notification_hook(previous->id, TRANSACTION_WRITTEN,
previous->notification_data);
}
@ -900,6 +973,8 @@ cache_end_transaction(void *_cache, int32 id,
transaction->notification_hook = hook;
transaction->notification_data = data;
notify_transaction_listeners(transaction, TRANSACTION_ENDED);
// iterate through all blocks and free the unchanged original contents
cached_block *block = transaction->first_block, *next;
@ -949,6 +1024,8 @@ cache_abort_transaction(void *_cache, int32 id)
return B_BAD_VALUE;
}
notify_transaction_listeners(transaction, TRANSACTION_ABORTED);
// iterate through all blocks and restore their original contents
cached_block *block = transaction->first_block, *next;
@ -1010,6 +1087,8 @@ cache_detach_sub_transaction(void *_cache, int32 id,
transaction->notification_hook = hook;
transaction->notification_data = data;
notify_transaction_listeners(transaction, TRANSACTION_ENDED);
// iterate through all blocks and free the unchanged original contents
cached_block *block = transaction->first_block, *next, *last = NULL;
@ -1074,6 +1153,8 @@ cache_abort_sub_transaction(void *_cache, int32 id)
if (!transaction->has_sub_transaction)
return B_BAD_VALUE;
notify_transaction_listeners(transaction, TRANSACTION_ABORTED);
// revert all changes back to the version of the parent
cached_block *block = transaction->first_block, *next;
@ -1118,6 +1199,8 @@ cache_start_sub_transaction(void *_cache, int32 id)
return B_BAD_VALUE;
}
notify_transaction_listeners(transaction, TRANSACTION_ENDED);
// move all changed blocks up to the parent
cached_block *block = transaction->first_block, *next;
@ -1145,6 +1228,62 @@ cache_start_sub_transaction(void *_cache, int32 id)
}
/*! Adds a transaction listener that gets notified when the transaction
is ended or aborted.
The listener gets automatically removed in this case.
*/
status_t
cache_add_transaction_listener(void *_cache, int32 id,
transaction_notification_hook hookFunction, void *data)
{
block_cache *cache = (block_cache *)_cache;
cache_hook *hook = new(std::nothrow) cache_hook;
if (hook == NULL)
return B_NO_MEMORY;
BenaphoreLocker locker(&cache->lock);
cache_transaction *transaction = lookup_transaction(cache, id);
if (transaction == NULL) {
delete hook;
return B_BAD_VALUE;
}
hook->hook = hookFunction;
hook->data = data;
transaction->listeners.Add(hook);
return B_OK;
}
status_t
cache_remove_transaction_listener(void *_cache, int32 id,
transaction_notification_hook hookFunction, void *data)
{
block_cache *cache = (block_cache *)_cache;
BenaphoreLocker locker(&cache->lock);
cache_transaction *transaction = lookup_transaction(cache, id);
if (transaction == NULL)
return B_BAD_VALUE;
HookList::Iterator iterator = transaction->listeners.GetIterator();
while (iterator.HasNext()) {
cache_hook *hook = iterator.Next();
if (hook->data == data && hook->hook == hookFunction) {
iterator.Remove();
delete hook;
return B_OK;
}
}
return B_ENTRY_NOT_FOUND;
}
extern "C" status_t
cache_next_block_in_transaction(void *_cache, int32 id, uint32 *_cookie,
off_t *_blockNumber, void **_data, void **_unchangedData)

View File

@ -1,5 +1,5 @@
/*
* Copyright 2004-2006, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
* Copyright 2004-2008, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
* Distributed under the terms of the MIT License.
*/
@ -45,6 +45,12 @@ namespace FSShell {
static const int32_t kMaxBlockCount = 1024;
struct cache_hook : DoublyLinkedListLinkImpl<cache_hook> {
fssh_transaction_notification_hook hook;
void *data;
};
typedef DoublyLinkedList<cache_hook> HookList;
struct cache_transaction {
cache_transaction();
@ -57,6 +63,7 @@ struct cache_transaction {
block_list blocks;
fssh_transaction_notification_hook notification_hook;
void *notification_data;
HookList listeners;
bool open;
bool has_sub_transaction;
};
@ -102,6 +109,24 @@ transaction_hash(void *_transaction, const void *_id, uint32_t range)
}
/*! Notifies all listeners of this transaction, and removes them
afterwards.
*/
static void
notify_transaction_listeners(cache_transaction *transaction, int32_t event)
{
HookList::Iterator iterator = transaction->listeners.GetIterator();
while (iterator.HasNext()) {
cache_hook *hook = iterator.Next();
hook->hook(transaction->id, event, hook->data);
iterator.Remove();
delete hook;
}
}
static void
delete_transaction(block_cache *cache, cache_transaction *transaction)
{
@ -570,7 +595,7 @@ write_cached_block(block_cache *cache, cached_block *block,
if (previous->notification_hook != NULL) {
previous->notification_hook(previous->id,
previous->notification_data);
FSSH_TRANSACTION_WRITTEN, previous->notification_data);
}
if (deleteTransaction) {
@ -678,6 +703,8 @@ fssh_cache_end_transaction(void *_cache, int32_t id,
transaction->notification_hook = hook;
transaction->notification_data = data;
notify_transaction_listeners(transaction, FSSH_TRANSACTION_ENDED);
// iterate through all blocks and free the unchanged original contents
cached_block *block = transaction->first_block, *next;
@ -727,6 +754,8 @@ fssh_cache_abort_transaction(void *_cache, int32_t id)
return FSSH_B_BAD_VALUE;
}
notify_transaction_listeners(transaction, FSSH_TRANSACTION_ABORTED);
// iterate through all blocks and restore their original contents
cached_block *block = transaction->first_block, *next;
@ -788,6 +817,8 @@ fssh_cache_detach_sub_transaction(void *_cache, int32_t id,
transaction->notification_hook = hook;
transaction->notification_data = data;
notify_transaction_listeners(transaction, FSSH_TRANSACTION_ENDED);
// iterate through all blocks and free the unchanged original contents
cached_block *block = transaction->first_block, *next, *last = NULL;
@ -852,6 +883,8 @@ fssh_cache_abort_sub_transaction(void *_cache, int32_t id)
if (!transaction->has_sub_transaction)
return FSSH_B_BAD_VALUE;
notify_transaction_listeners(transaction, FSSH_TRANSACTION_ABORTED);
// revert all changes back to the version of the parent
cached_block *block = transaction->first_block, *next;
@ -896,6 +929,8 @@ fssh_cache_start_sub_transaction(void *_cache, int32_t id)
return FSSH_B_BAD_VALUE;
}
notify_transaction_listeners(transaction, FSSH_TRANSACTION_ENDED);
// move all changed blocks up to the parent
cached_block *block = transaction->first_block, *next;
@ -923,6 +958,62 @@ fssh_cache_start_sub_transaction(void *_cache, int32_t id)
}
/*! Adds a transaction listener that gets notified when the transaction
is ended or aborted.
The listener gets automatically removed in this case.
*/
fssh_status_t
fssh_cache_add_transaction_listener(void *_cache, int32_t id,
fssh_transaction_notification_hook hookFunction, void *data)
{
block_cache *cache = (block_cache *)_cache;
cache_hook *hook = new(std::nothrow) cache_hook;
if (hook == NULL)
return FSSH_B_NO_MEMORY;
BenaphoreLocker locker(&cache->lock);
cache_transaction *transaction = lookup_transaction(cache, id);
if (transaction == NULL) {
delete hook;
return FSSH_B_BAD_VALUE;
}
hook->hook = hookFunction;
hook->data = data;
transaction->listeners.Add(hook);
return FSSH_B_OK;
}
fssh_status_t
fssh_cache_remove_transaction_listener(void *_cache, int32_t id,
fssh_transaction_notification_hook hookFunction, void *data)
{
block_cache *cache = (block_cache *)_cache;
BenaphoreLocker locker(&cache->lock);
cache_transaction *transaction = lookup_transaction(cache, id);
if (transaction == NULL)
return FSSH_B_BAD_VALUE;
HookList::Iterator iterator = transaction->listeners.GetIterator();
while (iterator.HasNext()) {
cache_hook *hook = iterator.Next();
if (hook->data == data && hook->hook == hookFunction) {
iterator.Remove();
delete hook;
return FSSH_B_OK;
}
}
return FSSH_B_ENTRY_NOT_FOUND;
}
fssh_status_t
fssh_cache_next_block_in_transaction(void *_cache, int32_t id, uint32_t *_cookie,
fssh_off_t *_blockNumber, void **_data, void **_unchangedData)