From 4fc4f2c8aeefd8548dc95f51af64beabc07a4ec5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Axel=20D=C3=B6rfler?= Date: Sun, 13 Jan 2008 17:18:29 +0000 Subject: [PATCH] * 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 --- headers/os/drivers/fs_cache.h | 15 +- headers/private/fs_shell/fssh_api_wrapper.h | 8 +- headers/private/fs_shell/fssh_fs_cache.h | 17 ++- .../file_systems/bfs/BlockAllocator.cpp | 2 +- src/add-ons/kernel/file_systems/bfs/Inode.cpp | 24 ++- .../kernel/file_systems/bfs/Journal.cpp | 9 +- src/add-ons/kernel/file_systems/bfs/Journal.h | 2 +- src/system/kernel/cache/block_cache.cpp | 143 +++++++++++++++++- src/tools/fs_shell/block_cache.cpp | 95 +++++++++++- 9 files changed, 298 insertions(+), 17 deletions(-) diff --git a/headers/os/drivers/fs_cache.h b/headers/os/drivers/fs_cache.h index 4aeb98dda9..7538b399dd 100644 --- a/headers/os/drivers/fs_cache.h +++ b/headers/os/drivers/fs_cache.h @@ -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 -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); diff --git a/headers/private/fs_shell/fssh_api_wrapper.h b/headers/private/fs_shell/fssh_api_wrapper.h index e2e0691d91..c506bdc259 100644 --- a/headers/private/fs_shell/fssh_api_wrapper.h +++ b/headers/private/fs_shell/fssh_api_wrapper.h @@ -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 diff --git a/headers/private/fs_shell/fssh_fs_cache.h b/headers/private/fs_shell/fssh_fs_cache.h index 335068761e..657c75bbcb 100644 --- a/headers/private/fs_shell/fssh_fs_cache.h +++ b/headers/private/fs_shell/fssh_fs_cache.h @@ -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, diff --git a/src/add-ons/kernel/file_systems/bfs/BlockAllocator.cpp b/src/add-ons/kernel/file_systems/bfs/BlockAllocator.cpp index 06daa40c90..32a8efea54 100644 --- a/src/add-ons/kernel/file_systems/bfs/BlockAllocator.cpp +++ b/src/add-ons/kernel/file_systems/bfs/BlockAllocator.cpp @@ -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()); } diff --git a/src/add-ons/kernel/file_systems/bfs/Inode.cpp b/src/add-ons/kernel/file_systems/bfs/Inode.cpp index 2aa5139697..0a4afb5af8 100644 --- a/src/add-ons/kernel/file_systems/bfs/Inode.cpp +++ b/src/add-ons/kernel/file_systems/bfs/Inode.cpp @@ -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 - diff --git a/src/add-ons/kernel/file_systems/bfs/Journal.cpp b/src/add-ons/kernel/file_systems/bfs/Journal.cpp index e7173911e4..939a9dfaaf 100644 --- a/src/add-ons/kernel/file_systems/bfs/Journal.cpp +++ b/src/add-ons/kernel/file_systems/bfs/Journal.cpp @@ -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 diff --git a/src/add-ons/kernel/file_systems/bfs/Journal.h b/src/add-ons/kernel/file_systems/bfs/Journal.h index bf77cc8be1..6a89bd0315 100644 --- a/src/add-ons/kernel/file_systems/bfs/Journal.h +++ b/src/add-ons/kernel/file_systems/bfs/Journal.h @@ -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; diff --git a/src/system/kernel/cache/block_cache.cpp b/src/system/kernel/cache/block_cache.cpp index 4c58b6101d..83de42dfde 100644 --- a/src/system/kernel/cache/block_cache.cpp +++ b/src/system/kernel/cache/block_cache.cpp @@ -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 #include #include +#include #include #include #include @@ -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 { + transaction_notification_hook hook; + void *data; +}; + +typedef DoublyLinkedList 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) diff --git a/src/tools/fs_shell/block_cache.cpp b/src/tools/fs_shell/block_cache.cpp index 881bac5f9e..6393d9806b 100644 --- a/src/tools/fs_shell/block_cache.cpp +++ b/src/tools/fs_shell/block_cache.cpp @@ -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 { + fssh_transaction_notification_hook hook; + void *data; +}; + +typedef DoublyLinkedList 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)