11f6dcd13SAxel Dörfler/*
293845aecSAxel Dörfler * Copyright 2004-2020, Axel D��rfler, axeld@pinc-software.de.
385b750f2SAxel Dörfler * Distributed under the terms of the MIT License.
485b750f2SAxel Dörfler */
56a3016d2SAxel Dörfler
66a3016d2SAxel Dörfler
70aee496bSAxel Dörfler#include <block_cache.h>
8d5b04e50SAxel Dörfler
96eabbacdSAxel Dörfler#include <unistd.h>
106eabbacdSAxel Dörfler#include <stdlib.h>
116eabbacdSAxel Dörfler#include <string.h>
126eabbacdSAxel Dörfler#include <errno.h>
136eabbacdSAxel Dörfler
146a3016d2SAxel Dörfler#include <KernelExport.h>
1585b750f2SAxel Dörfler#include <fs_cache.h>
166a3016d2SAxel Dörfler
17807d3aa8SAxel Dörfler#include <condition_variable.h>
186a3016d2SAxel Dörfler#include <lock.h>
195c99d639SIngo Weinhold#include <low_resource_manager.h>
200aee496bSAxel Dörfler#include <slab/Slab.h>
214fc4f2c8SAxel Dörfler#include <tracing.h>
2252fe8bf7SAxel Dörfler#include <util/kernel_cpp.h>
2352fe8bf7SAxel Dörfler#include <util/DoublyLinkedList.h>
24317121e1SAxel Dörfler#include <util/AutoLock.h>
250181fe4bSAxel Dörfler#include <vm/vm_page.h>
266a3016d2SAxel Dörfler
27f8bdc244SIngo Weinhold#include "kernel_debug_config.h"
28f8bdc244SIngo Weinhold
296a3016d2SAxel Dörfler
305c03270bSAxel Dörfler// TODO: this is a naive but growing implementation to test the API:
31c5e52ecbSAxel Dörfler//	block reading/writing is not at all optimized for speed, it will
32c5e52ecbSAxel Dörfler//	just read and write single blocks.
331bb74eb1SAxel Dörfler// TODO: the retrieval/copy of the original data could be delayed until the
341bb74eb1SAxel Dörfler//		new data must be written, ie. in low memory situations.
3585b750f2SAxel Dörfler
361c88c851SAxel Dörfler//#define TRACE_BLOCK_CACHE
376d6292cfSAxel Dörfler#ifdef TRACE_BLOCK_CACHE
386d6292cfSAxel Dörfler#	define TRACE(x)	dprintf x
396d6292cfSAxel Dörfler#else
406d6292cfSAxel Dörfler#	define TRACE(x) ;
416d6292cfSAxel Dörfler#endif
426d6292cfSAxel Dörfler
43efdcada6SIthamar R. Adema#define TRACE_ALWAYS(x) dprintf x
44efdcada6SIthamar R. Adema
45e075b5a3SAxel Dörfler// This macro is used for fatal situations that are acceptable in a running
46e075b5a3SAxel Dörfler// system, like out of memory situations - should only panic for debugging.
47e075b5a3SAxel Dörfler#define FATAL(x) panic x
486a3016d2SAxel Dörfler
4916d8ff2dSAxel Dörflerstatic const bigtime_t kTransactionIdleTime = 2000000LL;
5016d8ff2dSAxel Dörfler	// a transaction is considered idle after 2 seconds of inactivity
5116d8ff2dSAxel Dörfler
520aee496bSAxel Dörfler
53c73d1301SMichael Lotznamespace {
54c73d1301SMichael Lotz
550aee496bSAxel Dörflerstruct cache_transaction;
560aee496bSAxel Dörflerstruct cached_block;
570aee496bSAxel Dörflerstruct block_cache;
580aee496bSAxel Dörflertypedef DoublyLinkedListLink<cached_block> block_link;
590aee496bSAxel Dörfler
600aee496bSAxel Dörflerstruct cached_block {
61f69f65ecSAxel Dörfler	cached_block*	next;			// next in hash
62f69f65ecSAxel Dörfler	cached_block*	transaction_next;
630aee496bSAxel Dörfler	block_link		link;
640aee496bSAxel Dörfler	off_t			block_number;
65f69f65ecSAxel Dörfler	void*			current_data;
663426ce14SAxel Dörfler		// The data that is seen by everyone using the API; this one is always
673426ce14SAxel Dörfler		// present.
68f69f65ecSAxel Dörfler	void*			original_data;
693426ce14SAxel Dörfler		// When in a transaction, this contains the original data from before
703426ce14SAxel Dörfler		// the transaction.
71f69f65ecSAxel Dörfler	void*			parent_data;
723426ce14SAxel Dörfler		// This is a lazily alloced buffer that represents the contents of the
733426ce14SAxel Dörfler		// block in the parent transaction. It may point to current_data if the
743426ce14SAxel Dörfler		// contents have been changed only in the parent transaction, or, if the
753426ce14SAxel Dörfler		// block has been changed in the current sub transaction already, to a
763426ce14SAxel Dörfler		// new block containing the contents changed in the parent transaction.
773426ce14SAxel Dörfler		// If this is NULL, the block has not been changed in the parent
783426ce14SAxel Dörfler		// transaction at all.
7959dbd26fSIngo Weinhold#if BLOCK_CACHE_DEBUG_CHANGED
80f69f65ecSAxel Dörfler	void*			compare;
810aee496bSAxel Dörfler#endif
820aee496bSAxel Dörfler	int32			ref_count;
830181fe4bSAxel Dörfler	int32			last_accessed;
844ea83177SAxel Dörfler	bool			busy_reading : 1;
854ea83177SAxel Dörfler	bool			busy_writing : 1;
860aee496bSAxel Dörfler	bool			is_writing : 1;
8722991e1dSAxel Dörfler		// Block has been checked out for writing without transactions, and
8822991e1dSAxel Dörfler		// cannot be written back if set
890aee496bSAxel Dörfler	bool			is_dirty : 1;
900aee496bSAxel Dörfler	bool			unused : 1;
915b812019SAxel Dörfler	bool			discard : 1;
924ea83177SAxel Dörfler	bool			busy_reading_waiters : 1;
934ea83177SAxel Dörfler	bool			busy_writing_waiters : 1;
94f69f65ecSAxel Dörfler	cache_transaction* transaction;
950579a695SAxel Dörfler		// This is the current active transaction, if any, the block is
960579a695SAxel Dörfler		// currently in (meaning was changed as a part of it).
97f69f65ecSAxel Dörfler	cache_transaction* previous_transaction;
980579a695SAxel Dörfler		// This is set to the last transaction that was ended containing this
990579a695SAxel Dörfler		// block. In this case, the block has not yet written back yet, and
1000579a695SAxel Dörfler		// the changed data is either in current_data, or original_data -- the
1010579a695SAxel Dörfler		// latter if the block is already being part of another transaction.
1020579a695SAxel Dörfler		// There can only be one previous transaction, so when the active
1030579a695SAxel Dörfler		// transaction ends, the changes of the previous transaction have to
1040579a695SAxel Dörfler		// be written back before that transaction becomes the next previous
1050579a695SAxel Dörfler		// transaction.
1060aee496bSAxel Dörfler
1074ea83177SAxel Dörfler	bool CanBeWritten() const;
1080181fe4bSAxel Dörfler	int32 LastAccess() const
1090181fe4bSAxel Dörfler		{ return system_time() / 1000000L - last_accessed; }
1100aee496bSAxel Dörfler};
1110aee496bSAxel Dörfler
1120aee496bSAxel Dörflertypedef DoublyLinkedList<cached_block,
1130aee496bSAxel Dörfler	DoublyLinkedListMemberGetLink<cached_block,
1140aee496bSAxel Dörfler		&cached_block::link> > block_list;
1150aee496bSAxel Dörfler
116816957bdSAxel Dörflerstruct cache_notification : DoublyLinkedListLinkImpl<cache_notification> {
1170e6ece91SAugustin Cavalier	static inline void* operator new(size_t size);
1180e6ece91SAugustin Cavalier	static inline void operator delete(void* block);
1190e6ece91SAugustin Cavalier
120816957bdSAxel Dörfler	int32			transaction_id;
121816957bdSAxel Dörfler	int32			events_pending;
122816957bdSAxel Dörfler	int32			events;
123816957bdSAxel Dörfler	transaction_notification_hook hook;
124f69f65ecSAxel Dörfler	void*			data;
125816957bdSAxel Dörfler	bool			delete_after_event;
126816957bdSAxel Dörfler};
127816957bdSAxel Dörfler
128816957bdSAxel Dörflertypedef DoublyLinkedList<cache_notification> NotificationList;
129816957bdSAxel Dörfler
1300e6ece91SAugustin Cavalierstatic object_cache* sCacheNotificationCache;
1310e6ece91SAugustin Cavalier
1320e6ece91SAugustin Cavalierstruct cache_listener;
1330e6ece91SAugustin Cavaliertypedef DoublyLinkedListLink<cache_listener> listener_link;
1340e6ece91SAugustin Cavalier
1350e6ece91SAugustin Cavalierstruct cache_listener : cache_notification {
1360e6ece91SAugustin Cavalier	listener_link	link;
1370e6ece91SAugustin Cavalier};
1380e6ece91SAugustin Cavalier
1390e6ece91SAugustin Cavaliertypedef DoublyLinkedList<cache_listener,
1400e6ece91SAugustin Cavalier	DoublyLinkedListMemberGetLink<cache_listener,
1410e6ece91SAugustin Cavalier		&cache_listener::link> > ListenerList;
1420e6ece91SAugustin Cavalier
1430e6ece91SAugustin Cavaliervoid*
1440e6ece91SAugustin Cavaliercache_notification::operator new(size_t size)
1450e6ece91SAugustin Cavalier{
1460e6ece91SAugustin Cavalier	// We can't really know whether something is a cache_notification or a
1470e6ece91SAugustin Cavalier	// cache_listener at runtime, so we just use one object_cache for both
1480e6ece91SAugustin Cavalier	// with the size set to that of the (slightly larger) cache_listener.
1490e6ece91SAugustin Cavalier	// In practice, the vast majority of cache_notifications are really
1500e6ece91SAugustin Cavalier	// cache_listeners, so this is a more than acceptable trade-off.
1510e6ece91SAugustin Cavalier	ASSERT(size <= sizeof(cache_listener));
1520e6ece91SAugustin Cavalier	return object_cache_alloc(sCacheNotificationCache, 0);
1530e6ece91SAugustin Cavalier}
1540e6ece91SAugustin Cavalier
1550e6ece91SAugustin Cavaliervoid
1560e6ece91SAugustin Cavaliercache_notification::operator delete(void* block)
1570e6ece91SAugustin Cavalier{
1580e6ece91SAugustin Cavalier	object_cache_free(sCacheNotificationCache, block, 0);
1590e6ece91SAugustin Cavalier}
1600e6ece91SAugustin Cavalier
1610e6ece91SAugustin Cavalier
1629d1c3b8dSAdrien Destuguesstruct BlockHash {
1639d1c3b8dSAdrien Destugues	typedef off_t			KeyType;
1649d1c3b8dSAdrien Destugues	typedef	cached_block	ValueType;
1659d1c3b8dSAdrien Destugues
1669d1c3b8dSAdrien Destugues	size_t HashKey(KeyType key) const
1679d1c3b8dSAdrien Destugues	{
1689d1c3b8dSAdrien Destugues		return key;
1699d1c3b8dSAdrien Destugues	}
1709d1c3b8dSAdrien Destugues
1719d1c3b8dSAdrien Destugues	size_t Hash(ValueType* block) const
1729d1c3b8dSAdrien Destugues	{
1739d1c3b8dSAdrien Destugues		return block->block_number;
1749d1c3b8dSAdrien Destugues	}
1759d1c3b8dSAdrien Destugues
1769d1c3b8dSAdrien Destugues	bool Compare(KeyType key, ValueType* block) const
1779d1c3b8dSAdrien Destugues	{
1789d1c3b8dSAdrien Destugues		return block->block_number == key;
1799d1c3b8dSAdrien Destugues	}
1809d1c3b8dSAdrien Destugues
1819d1c3b8dSAdrien Destugues	ValueType*& GetLink(ValueType* value) const
1829d1c3b8dSAdrien Destugues	{
1839d1c3b8dSAdrien Destugues		return value->next;
1849d1c3b8dSAdrien Destugues	}
1859d1c3b8dSAdrien Destugues};
1869d1c3b8dSAdrien Destugues
1879d1c3b8dSAdrien Destuguestypedef BOpenHashTable<BlockHash> BlockTable;
1889d1c3b8dSAdrien Destugues
1899d1c3b8dSAdrien Destugues
1909d1c3b8dSAdrien Destuguesstruct TransactionHash {
1919d1c3b8dSAdrien Destugues	typedef int32				KeyType;
1929d1c3b8dSAdrien Destugues	typedef	cache_transaction	ValueType;
1939d1c3b8dSAdrien Destugues
1949d1c3b8dSAdrien Destugues	size_t HashKey(KeyType key) const
1959d1c3b8dSAdrien Destugues	{
1969d1c3b8dSAdrien Destugues		return key;
1979d1c3b8dSAdrien Destugues	}
1989d1c3b8dSAdrien Destugues
1999d1c3b8dSAdrien Destugues	size_t Hash(ValueType* transaction) const;
2009d1c3b8dSAdrien Destugues	bool Compare(KeyType key, ValueType* transaction) const;
2019d1c3b8dSAdrien Destugues	ValueType*& GetLink(ValueType* value) const;
2029d1c3b8dSAdrien Destugues};
2039d1c3b8dSAdrien Destugues
2049d1c3b8dSAdrien Destuguestypedef BOpenHashTable<TransactionHash> TransactionTable;
2059d1c3b8dSAdrien Destugues
2069d1c3b8dSAdrien Destugues
2070aee496bSAxel Dörflerstruct block_cache : DoublyLinkedListLinkImpl<block_cache> {
2089d1c3b8dSAdrien Destugues	BlockTable*		hash;
209adf376c9SAxel Dörfler	mutex			lock;
2100aee496bSAxel Dörfler	int				fd;
2110aee496bSAxel Dörfler	off_t			max_blocks;
2120aee496bSAxel Dörfler	size_t			block_size;
2130aee496bSAxel Dörfler	int32			next_transaction_id;
214f69f65ecSAxel Dörfler	cache_transaction* last_transaction;
2159d1c3b8dSAdrien Destugues	TransactionTable* transaction_hash;
2160aee496bSAxel Dörfler
217f69f65ecSAxel Dörfler	object_cache*	buffer_cache;
2180aee496bSAxel Dörfler	block_list		unused_blocks;
2191cafaecaSIngo Weinhold	uint32			unused_block_count;
2200aee496bSAxel Dörfler
2214ea83177SAxel Dörfler	ConditionVariable busy_reading_condition;
222c5e52ecbSAxel Dörfler	uint32			busy_reading_count;
2234ea83177SAxel Dörfler	bool			busy_reading_waiters;
2244ea83177SAxel Dörfler
2254ea83177SAxel Dörfler	ConditionVariable busy_writing_condition;
2264ea83177SAxel Dörfler	uint32			busy_writing_count;
2274ea83177SAxel Dörfler	bool			busy_writing_waiters;
22833c82a30SAxel Dörfler
2296d336fdaSAugustin Cavalier	bigtime_t		last_block_write;
2306d336fdaSAugustin Cavalier	bigtime_t		last_block_write_duration;
2316d336fdaSAugustin Cavalier
2320aee496bSAxel Dörfler	uint32			num_dirty_blocks;
2330aee496bSAxel Dörfler	bool			read_only;
2340aee496bSAxel Dörfler
235816957bdSAxel Dörfler	NotificationList pending_notifications;
2366cef245eSIngo Weinhold	ConditionVariable condition_variable;
237816957bdSAxel Dörfler
23896e19c19SAxel Dörfler					block_cache(int fd, off_t numBlocks, size_t blockSize,
23996e19c19SAxel Dörfler						bool readOnly);
24096e19c19SAxel Dörfler					~block_cache();
2410aee496bSAxel Dörfler
24296e19c19SAxel Dörfler	status_t		Init();
2430aee496bSAxel Dörfler
24496e19c19SAxel Dörfler	void			Free(void* buffer);
24596e19c19SAxel Dörfler	void*			Allocate();
24696e19c19SAxel Dörfler	void			FreeBlock(cached_block* block);
24796e19c19SAxel Dörfler	cached_block*	NewBlock(off_t blockNumber);
2488f9dac69SAxel Dörfler	void			FreeBlockParentData(cached_block* block);
2490aee496bSAxel Dörfler
2508f9dac69SAxel Dörfler	void			RemoveUnusedBlocks(int32 count, int32 minSecondsOld = 0);
2518f9dac69SAxel Dörfler	void			RemoveBlock(cached_block* block);
2528f9dac69SAxel Dörfler	void			DiscardBlock(cached_block* block);
2530aee496bSAxel Dörfler
2540aee496bSAxel Dörflerprivate:
25596e19c19SAxel Dörfler	static void		_LowMemoryHandler(void* data, uint32 resources,
25696e19c19SAxel Dörfler						int32 level);
25796e19c19SAxel Dörfler	cached_block*	_GetUnusedBlock();
2580aee496bSAxel Dörfler};
2590aee496bSAxel Dörfler
260502c4640SAxel Dörflerstruct cache_transaction {
261502c4640SAxel Dörfler	cache_transaction();
262502c4640SAxel Dörfler
263f69f65ecSAxel Dörfler	cache_transaction* next;
264502c4640SAxel Dörfler	int32			id;
265502c4640SAxel Dörfler	int32			num_blocks;
2665d0afa4eSAxel Dörfler	int32			main_num_blocks;
267502c4640SAxel Dörfler	int32			sub_num_blocks;
268f69f65ecSAxel Dörfler	cached_block*	first_block;
269502c4640SAxel Dörfler	block_list		blocks;
270816957bdSAxel Dörfler	ListenerList	listeners;
271502c4640SAxel Dörfler	bool			open;
272502c4640SAxel Dörfler	bool			has_sub_transaction;
27316d8ff2dSAxel Dörfler	bigtime_t		last_used;
274c5e52ecbSAxel Dörfler	int32			busy_writing_count;
275502c4640SAxel Dörfler};
276502c4640SAxel Dörfler
2774ea83177SAxel Dörfler
2784ea83177SAxel Dörflerclass BlockWriter {
2794ea83177SAxel Dörflerpublic:
2804ea83177SAxel Dörfler								BlockWriter(block_cache* cache,
281c5e52ecbSAxel Dörfler									size_t max = SIZE_MAX);
2824ea83177SAxel Dörfler								~BlockWriter();
2834ea83177SAxel Dörfler
284c5e52ecbSAxel Dörfler			bool				Add(cached_block* block,
2859d1c3b8dSAdrien Destugues									cache_transaction* transaction = NULL);
286c5e52ecbSAxel Dörfler			bool				Add(cache_transaction* transaction,
2879d071e27SAxel Dörfler									bool& hasLeftOvers);
2884ea83177SAxel Dörfler
2899d1c3b8dSAdrien Destugues			status_t			Write(cache_transaction* transaction = NULL,
290c5e52ecbSAxel Dörfler									bool canUnlock = true);
291c5e52ecbSAxel Dörfler
292c5e52ecbSAxel Dörfler			bool				DeletedTransaction() const
293c5e52ecbSAxel Dörfler									{ return fDeletedTransaction; }
294c5e52ecbSAxel Dörfler
295c5e52ecbSAxel Dörfler	static	status_t			WriteBlock(block_cache* cache,
296c5e52ecbSAxel Dörfler									cached_block* block);
2974ea83177SAxel Dörfler
2984ea83177SAxel Dörflerprivate:
2994ea83177SAxel Dörfler			void*				_Data(cached_block* block) const;
3004ea83177SAxel Dörfler			status_t			_WriteBlock(cached_block* block);
301c5e52ecbSAxel Dörfler			void				_BlockDone(cached_block* block,
3029d1c3b8dSAdrien Destugues									cache_transaction* transaction);
303c5e52ecbSAxel Dörfler			void				_UnmarkWriting(cached_block* block);
3044ea83177SAxel Dörfler
3058f9dac69SAxel Dörfler	static	int					_CompareBlocks(const void* _blockA,
3068f9dac69SAxel Dörfler									const void* _blockB);
3078f9dac69SAxel Dörfler
3084ea83177SAxel Dörflerprivate:
309c5e52ecbSAxel Dörfler	static	const size_t		kBufferSize = 64;
3104ea83177SAxel Dörfler
3114ea83177SAxel Dörfler			block_cache*		fCache;
3124ea83177SAxel Dörfler			cached_block*		fBuffer[kBufferSize];
3134ea83177SAxel Dörfler			cached_block**		fBlocks;
3144ea83177SAxel Dörfler			size_t				fCount;
315c5e52ecbSAxel Dörfler			size_t				fTotal;
316c5e52ecbSAxel Dörfler			size_t				fCapacity;
3174ea83177SAxel Dörfler			size_t				fMax;
318c5e52ecbSAxel Dörfler			status_t			fStatus;
319c5e52ecbSAxel Dörfler			bool				fDeletedTransaction;
3204ea83177SAxel Dörfler};
3214ea83177SAxel Dörfler
3224ea83177SAxel Dörfler
3234ea83177SAxel Dörflerclass TransactionLocking {
3244ea83177SAxel Dörflerpublic:
3254ea83177SAxel Dörfler	inline bool Lock(block_cache* cache)
3264ea83177SAxel Dörfler	{
3274ea83177SAxel Dörfler		mutex_lock(&cache->lock);
3284ea83177SAxel Dörfler
3294ea83177SAxel Dörfler		while (cache->busy_writing_count != 0) {
3304ea83177SAxel Dörfler			// wait for all blocks to be written
3314ea83177SAxel Dörfler			ConditionVariableEntry entry;
3324ea83177SAxel Dörfler			cache->busy_writing_condition.Add(&entry);
3334ea83177SAxel Dörfler			cache->busy_writing_waiters = true;
3344ea83177SAxel Dörfler
3354ea83177SAxel Dörfler			mutex_unlock(&cache->lock);
3364ea83177SAxel Dörfler
3374ea83177SAxel Dörfler			entry.Wait();
3384ea83177SAxel Dörfler
3394ea83177SAxel Dörfler			mutex_lock(&cache->lock);
3404ea83177SAxel Dörfler		}
3414ea83177SAxel Dörfler
3424ea83177SAxel Dörfler		return true;
3434ea83177SAxel Dörfler	}
3444ea83177SAxel Dörfler
3454ea83177SAxel Dörfler	inline void Unlock(block_cache* cache)
3464ea83177SAxel Dörfler	{
3474ea83177SAxel Dörfler		mutex_unlock(&cache->lock);
3484ea83177SAxel Dörfler	}
3494ea83177SAxel Dörfler};
3504ea83177SAxel Dörfler
3514ea83177SAxel Dörflertypedef AutoLocker<block_cache, TransactionLocking> TransactionLocker;
3524ea83177SAxel Dörfler
353c73d1301SMichael Lotz} // namespace
354c73d1301SMichael Lotz
3554ea83177SAxel Dörfler
3570d066deaSAxel Dörflernamespace BlockTracing {
3580d066deaSAxel Dörfler
3590d066deaSAxel Dörflerclass Action : public AbstractTraceEntry {
3600d066deaSAxel Dörflerpublic:
361f69f65ecSAxel Dörfler	Action(block_cache* cache, cached_block* block)
3620d066deaSAxel Dörfler		:
3630d066deaSAxel Dörfler		fCache(cache),
3640d066deaSAxel Dörfler		fBlockNumber(block->block_number),
3650d066deaSAxel Dörfler		fIsDirty(block->is_dirty),
3660d066deaSAxel Dörfler		fHasOriginal(block->original_data != NULL),
3670d066deaSAxel Dörfler		fHasParent(block->parent_data != NULL),
3680d066deaSAxel Dörfler		fTransactionID(-1),
3690d066deaSAxel Dörfler		fPreviousID(-1)
3700d066deaSAxel Dörfler	{
3710d066deaSAxel Dörfler		if (block->transaction != NULL)
3720d066deaSAxel Dörfler			fTransactionID = block->transaction->id;
3730d066deaSAxel Dörfler		if (block->previous_transaction != NULL)
3740d066deaSAxel Dörfler			fPreviousID = block->previous_transaction->id;
3750d066deaSAxel Dörfler	}
3760d066deaSAxel Dörfler
3770d066deaSAxel Dörfler	virtual void AddDump(TraceOutput& out)
3780d066deaSAxel Dörfler	{
3790e88a887SAlex Smith		out.Print("block cache %p, %s %" B_PRIu64 ", %c%c%c transaction %" B_PRId32
3800e88a887SAlex Smith			" (previous id %" B_PRId32 ")\n", fCache, _Action(), fBlockNumber,
3810d066deaSAxel Dörfler			fIsDirty ? 'd' : '-', fHasOriginal ? 'o' : '-',
3820d066deaSAxel Dörfler			fHasParent ? 'p' : '-', fTransactionID, fPreviousID);
3830d066deaSAxel Dörfler	}
3840d066deaSAxel Dörfler
3850d066deaSAxel Dörfler	virtual const char* _Action() const = 0;
3860d066deaSAxel Dörfler
3870d066deaSAxel Dörflerprivate:
388f69f65ecSAxel Dörfler	block_cache*		fCache;
3890d066deaSAxel Dörfler	uint64				fBlockNumber;
3900d066deaSAxel Dörfler	bool				fIsDirty;
3910d066deaSAxel Dörfler	bool				fHasOriginal;
3920d066deaSAxel Dörfler	bool				fHasParent;
3930d066deaSAxel Dörfler	int32				fTransactionID;
3940d066deaSAxel Dörfler	int32				fPreviousID;
3950d066deaSAxel Dörfler};
3960d066deaSAxel Dörfler
3970d066deaSAxel Dörflerclass Get : public Action {
3980d066deaSAxel Dörflerpublic:
399f69f65ecSAxel Dörfler	Get(block_cache* cache, cached_block* block)
40061efcc32SAxel Dörfler		:
40161efcc32SAxel Dörfler		Action(cache, block)
4020d066deaSAxel Dörfler	{
4030d066deaSAxel Dörfler		Initialized();
4040d066deaSAxel Dörfler	}
4050d066deaSAxel Dörfler
4060d066deaSAxel Dörfler	virtual const char* _Action() const { return "get"; }
4070d066deaSAxel Dörfler};
4080d066deaSAxel Dörfler
4090d066deaSAxel Dörflerclass Put : public Action {
4100d066deaSAxel Dörflerpublic:
411f69f65ecSAxel Dörfler	Put(block_cache* cache, cached_block* block)
41261efcc32SAxel Dörfler		:
41361efcc32SAxel Dörfler		Action(cache, block)
4140d066deaSAxel Dörfler	{
4150d066deaSAxel Dörfler		Initialized();
4160d066deaSAxel Dörfler	}
4170d066deaSAxel Dörfler
4180d066deaSAxel Dörfler	virtual const char* _Action() const { return "put"; }
4190d066deaSAxel Dörfler};
4200d066deaSAxel Dörfler
4212f14f5eeSIngo Weinholdclass Read : public Action {
4222f14f5eeSIngo Weinholdpublic:
423f69f65ecSAxel Dörfler	Read(block_cache* cache, cached_block* block)
42461efcc32SAxel Dörfler		:
42561efcc32SAxel Dörfler		Action(cache, block)
4262f14f5eeSIngo Weinhold	{
4272f14f5eeSIngo Weinhold		Initialized();
4282f14f5eeSIngo Weinhold	}
4292f14f5eeSIngo Weinhold
4302f14f5eeSIngo Weinhold	virtual const char* _Action() const { return "read"; }
4312f14f5eeSIngo Weinhold};
4322f14f5eeSIngo Weinhold
4330d066deaSAxel Dörflerclass Write : public Action {
4340d066deaSAxel Dörflerpublic:
435f69f65ecSAxel Dörfler	Write(block_cache* cache, cached_block* block)
43661efcc32SAxel Dörfler		:
43761efcc32SAxel Dörfler		Action(cache, block)
4380d066deaSAxel Dörfler	{
4390d066deaSAxel Dörfler		Initialized();
4400d066deaSAxel Dörfler	}
4410d066deaSAxel Dörfler
4420d066deaSAxel Dörfler	virtual const char* _Action() const { return "write"; }
4430d066deaSAxel Dörfler};
4440d066deaSAxel Dörfler
4450d066deaSAxel Dörflerclass Flush : public Action {
4460d066deaSAxel Dörflerpublic:
447f69f65ecSAxel Dörfler	Flush(block_cache* cache, cached_block* block, bool getUnused = false)
44861efcc32SAxel Dörfler		:
44961efcc32SAxel Dörfler		Action(cache, block),
4500d066deaSAxel Dörfler		fGetUnused(getUnused)
4510d066deaSAxel Dörfler	{
4520d066deaSAxel Dörfler		Initialized();
4530d066deaSAxel Dörfler	}
4540d066deaSAxel Dörfler
4550d066deaSAxel Dörfler	virtual const char* _Action() const
4560d066deaSAxel Dörfler		{ return fGetUnused ? "get-unused" : "flush"; }
4570d066deaSAxel Dörfler
4580d066deaSAxel Dörflerprivate:
4590d066deaSAxel Dörfler	bool	fGetUnused;
4600d066deaSAxel Dörfler};
4610d066deaSAxel Dörfler
4620d066deaSAxel Dörflerclass Error : public AbstractTraceEntry {
4630d066deaSAxel Dörflerpublic:
464f69f65ecSAxel Dörfler	Error(block_cache* cache, uint64 blockNumber, const char* message,
4650d066deaSAxel Dörfler			status_t status = B_OK)
4660d066deaSAxel Dörfler		:
4670d066deaSAxel Dörfler		fCache(cache),
4680d066deaSAxel Dörfler		fBlockNumber(blockNumber),
4690d066deaSAxel Dörfler		fMessage(message),
4700d066deaSAxel Dörfler		fStatus(status)
4710d066deaSAxel Dörfler	{
4720d066deaSAxel Dörfler		Initialized();
4730d066deaSAxel Dörfler	}
4740d066deaSAxel Dörfler
4750d066deaSAxel Dörfler	virtual void AddDump(TraceOutput& out)
4760d066deaSAxel Dörfler	{
4770e88a887SAlex Smith		out.Print("block cache %p, error %" B_PRIu64 ", %s%s%s",
4780d066deaSAxel Dörfler			fCache, fBlockNumber, fMessage, fStatus != B_OK ? ": " : "",
4790d066deaSAxel Dörfler			fStatus != B_OK ? strerror(fStatus) : "");
4800d066deaSAxel Dörfler	}
4810d066deaSAxel Dörfler
4820d066deaSAxel Dörflerprivate:
4830d066deaSAxel Dörfler	block_cache*	fCache;
4840d066deaSAxel Dörfler	uint64			fBlockNumber;
4850d066deaSAxel Dörfler	const char*		fMessage;
4860d066deaSAxel Dörfler	status_t		fStatus;
4870d066deaSAxel Dörfler};
4880d066deaSAxel Dörfler
489d1cad268SAxel Dörfler#if BLOCK_CACHE_BLOCK_TRACING >= 2
490d1cad268SAxel Dörflerclass BlockData : public AbstractTraceEntry {
491d1cad268SAxel Dörflerpublic:
492d1cad268SAxel Dörfler	enum {
493d1cad268SAxel Dörfler		kCurrent	= 0x01,
494d1cad268SAxel Dörfler		kParent		= 0x02,
495d1cad268SAxel Dörfler		kOriginal	= 0x04
496d1cad268SAxel Dörfler	};
497d1cad268SAxel Dörfler
498d1cad268SAxel Dörfler	BlockData(block_cache* cache, cached_block* block, const char* message)
499d1cad268SAxel Dörfler		:
500d1cad268SAxel Dörfler		fCache(cache),
501d1cad268SAxel Dörfler		fSize(cache->block_size),
502d1cad268SAxel Dörfler		fBlockNumber(block->block_number),
503d1cad268SAxel Dörfler		fMessage(message)
504d1cad268SAxel Dörfler	{
505d1cad268SAxel Dörfler		_Allocate(fCurrent, block->current_data);
506d1cad268SAxel Dörfler		_Allocate(fParent, block->parent_data);
507d1cad268SAxel Dörfler		_Allocate(fOriginal, block->original_data);
508d1cad268SAxel Dörfler
509813d9285SAxel Dörfler#if KTRACE_PRINTF_STACK_TRACE
510813d9285SAxel Dörfler		fStackTrace = capture_tracing_stack_trace(KTRACE_PRINTF_STACK_TRACE, 1,
511813d9285SAxel Dörfler			false);
512813d9285SAxel Dörfler#endif
513813d9285SAxel Dörfler
514d1cad268SAxel Dörfler		Initialized();
515d1cad268SAxel Dörfler	}
516d1cad268SAxel Dörfler
517d1cad268SAxel Dörfler	virtual void AddDump(TraceOutput& out)
518d1cad268SAxel Dörfler	{
5190e88a887SAlex Smith		out.Print("block cache %p, block %" B_PRIu64 ", data %c%c%c: %s",
520d1cad268SAxel Dörfler			fCache, fBlockNumber, fCurrent != NULL ? 'c' : '-',
521d1cad268SAxel Dörfler			fParent != NULL ? 'p' : '-', fOriginal != NULL ? 'o' : '-',
522d1cad268SAxel Dörfler			fMessage);
523d1cad268SAxel Dörfler	}
524d1cad268SAxel Dörfler
525813d9285SAxel Dörfler#if KTRACE_PRINTF_STACK_TRACE
526813d9285SAxel Dörfler	virtual void DumpStackTrace(TraceOutput& out)
527813d9285SAxel Dörfler	{
528813d9285SAxel Dörfler		out.PrintStackTrace(fStackTrace);
529813d9285SAxel Dörfler	}
530813d9285SAxel Dörfler#endif
531813d9285SAxel Dörfler
532d1cad268SAxel Dörfler	void DumpBlocks(uint32 which, uint32 offset, uint32 size)
533d1cad268SAxel Dörfler	{
534d1cad268SAxel Dörfler		if ((which & kCurrent) != 0)
535d1cad268SAxel Dörfler			DumpBlock(kCurrent, offset, size);
536d1cad268SAxel Dörfler		if ((which & kParent) != 0)
537d1cad268SAxel Dörfler			DumpBlock(kParent, offset, size);
538d1cad268SAxel Dörfler		if ((which & kOriginal) != 0)
539d1cad268SAxel Dörfler			DumpBlock(kOriginal, offset, size);
540d1cad268SAxel Dörfler	}
541d1cad268SAxel Dörfler
542d1cad268SAxel Dörfler	void DumpBlock(uint32 which, uint32 offset, uint32 size)
543d1cad268SAxel Dörfler	{
544d1cad268SAxel Dörfler		if (offset > fSize) {
5450e88a887SAlex Smith			kprintf("invalid offset (block size %" B_PRIu32 ")\n", fSize);
546d1cad268SAxel Dörfler			return;
547d1cad268SAxel Dörfler		}
548d1cad268SAxel Dörfler		if (offset + size > fSize)
549d1cad268SAxel Dörfler			size = fSize - offset;
550d1cad268SAxel Dörfler
551d1cad268SAxel Dörfler		const char* label;
552d1cad268SAxel Dörfler		uint8* data;
553d1cad268SAxel Dörfler
554d1cad268SAxel Dörfler		if ((which & kCurrent) != 0) {
555d1cad268SAxel Dörfler			label = "current";
556d1cad268SAxel Dörfler			data = fCurrent;
557d1cad268SAxel Dörfler		} else if ((which & kParent) != 0) {
558d1cad268SAxel Dörfler			label = "parent";
559d1cad268SAxel Dörfler			data = fParent;
560d1cad268SAxel Dörfler		} else if ((which & kOriginal) != 0) {
561d1cad268SAxel Dörfler			label = "original";
562d1cad268SAxel Dörfler			data = fOriginal;
563d1cad268SAxel Dörfler		} else
564d1cad268SAxel Dörfler			return;
565d1cad268SAxel Dörfler
5660e88a887SAlex Smith		kprintf("%s: offset %" B_PRIu32 ", %" B_PRIu32 " bytes\n", label, offset, size);
567d1cad268SAxel Dörfler
568d1cad268SAxel Dörfler		static const uint32 kBlockSize = 16;
569d1cad268SAxel Dörfler		data += offset;
570d1cad268SAxel Dörfler
571d1cad268SAxel Dörfler		for (uint32 i = 0; i < size;) {
572d1cad268SAxel Dörfler			int start = i;
573d1cad268SAxel Dörfler
5740e88a887SAlex Smith			kprintf("  %04" B_PRIx32 " ", i);
575d1cad268SAxel Dörfler			for (; i < start + kBlockSize; i++) {
576d1cad268SAxel Dörfler				if (!(i % 4))
577d1cad268SAxel Dörfler					kprintf(" ");
578813d9285SAxel Dörfler
579d1cad268SAxel Dörfler				if (i >= size)
580d1cad268SAxel Dörfler					kprintf("  ");
581d1cad268SAxel Dörfler				else
582d1cad268SAxel Dörfler					kprintf("%02x", data[i]);
583d1cad268SAxel Dörfler			}
584d1cad268SAxel Dörfler
585d1cad268SAxel Dörfler			kprintf("\n");
586d1cad268SAxel Dörfler		}
587d1cad268SAxel Dörfler	}
588d1cad268SAxel Dörfler
589d1cad268SAxel Dörflerprivate:
590d1cad268SAxel Dörfler	void _Allocate(uint8*& target, void* source)
591d1cad268SAxel Dörfler	{
592d1cad268SAxel Dörfler		if (source == NULL) {
593d1cad268SAxel Dörfler			target = NULL;
594d1cad268SAxel Dörfler			return;
595d1cad268SAxel Dörfler		}
596d1cad268SAxel Dörfler
597d1cad268SAxel Dörfler		target = alloc_tracing_buffer_memcpy(source, fSize, false);
598d1cad268SAxel Dörfler	}
599d1cad268SAxel Dörfler
600d1cad268SAxel Dörfler	block_cache*	fCache;
601d1cad268SAxel Dörfler	uint32			fSize;
602d1cad268SAxel Dörfler	uint64			fBlockNumber;
603d1cad268SAxel Dörfler	const char*		fMessage;
604d1cad268SAxel Dörfler	uint8*			fCurrent;
605d1cad268SAxel Dörfler	uint8*			fParent;
606d1cad268SAxel Dörfler	uint8*			fOriginal;
607813d9285SAxel Dörfler#if KTRACE_PRINTF_STACK_TRACE
608813d9285SAxel Dörfler	tracing_stack_trace* fStackTrace;
609813d9285SAxel Dörfler#endif
610d1cad268SAxel Dörfler};
611d1cad268SAxel Dörfler#endif	// BLOCK_CACHE_BLOCK_TRACING >= 2
612d1cad268SAxel Dörfler
6130d066deaSAxel Dörfler}	// namespace BlockTracing
6140d066deaSAxel Dörfler
6150d066deaSAxel Dörfler#	define TB(x) new(std::nothrow) BlockTracing::x;
6160d066deaSAxel Dörfler#else
6170d066deaSAxel Dörfler#	define TB(x) ;
6180d066deaSAxel Dörfler#endif
6190d066deaSAxel Dörfler
620d1cad268SAxel Dörfler#if BLOCK_CACHE_BLOCK_TRACING >= 2
621d1cad268SAxel Dörfler#	define TB2(x) new(std::nothrow) BlockTracing::x;
622d1cad268SAxel Dörfler#else
623d1cad268SAxel Dörfler#	define TB2(x) ;
624d1cad268SAxel Dörfler#endif
625d1cad268SAxel Dörfler
626d1cad268SAxel Dörfler
6284fc4f2c8SAxel Dörflernamespace TransactionTracing {
6294fc4f2c8SAxel Dörfler
6305f7a62cfSAxel Dörflerclass Action : public AbstractTraceEntry {
63161efcc32SAxel Dörflerpublic:
63261efcc32SAxel Dörfler	Action(const char* label, block_cache* cache,
63361efcc32SAxel Dörfler			cache_transaction* transaction)
63461efcc32SAxel Dörfler		:
63561efcc32SAxel Dörfler		fCache(cache),
63661efcc32SAxel Dörfler		fTransaction(transaction),
63761efcc32SAxel Dörfler		fID(transaction->id),
63861efcc32SAxel Dörfler		fSub(transaction->has_sub_transaction),
63961efcc32SAxel Dörfler		fNumBlocks(transaction->num_blocks),
64061efcc32SAxel Dörfler		fSubNumBlocks(transaction->sub_num_blocks)
64161efcc32SAxel Dörfler	{
64261efcc32SAxel Dörfler		strlcpy(fLabel, label, sizeof(fLabel));
64361efcc32SAxel Dörfler		Initialized();
64461efcc32SAxel Dörfler	}
6454fc4f2c8SAxel Dörfler
64661efcc32SAxel Dörfler	virtual void AddDump(TraceOutput& out)
64761efcc32SAxel Dörfler	{
6480e88a887SAlex Smith		out.Print("block cache %p, %s transaction %p (id %" B_PRId32 ")%s"
6490e88a887SAlex Smith			", %" B_PRId32 "/%" B_PRId32 " blocks", fCache, fLabel, fTransaction,
6500e88a887SAlex Smith			fID, fSub ? " sub" : "", fNumBlocks, fSubNumBlocks);
65161efcc32SAxel Dörfler	}
6524fc4f2c8SAxel Dörfler
65361efcc32SAxel Dörflerprivate:
65461efcc32SAxel Dörfler	char				fLabel[12];
65561efcc32SAxel Dörfler	block_cache*		fCache;
65661efcc32SAxel Dörfler	cache_transaction*	fTransaction;
65761efcc32SAxel Dörfler	int32				fID;
65861efcc32SAxel Dörfler	bool				fSub;
65961efcc32SAxel Dörfler	int32				fNumBlocks;
66061efcc32SAxel Dörfler	int32				fSubNumBlocks;
6614fc4f2c8SAxel Dörfler};
6624fc4f2c8SAxel Dörfler
663502c4640SAxel Dörflerclass Detach : public AbstractTraceEntry {
66461efcc32SAxel Dörflerpublic:
66561efcc32SAxel Dörfler	Detach(block_cache* cache, cache_transaction* transaction,
66661efcc32SAxel Dörfler			cache_transaction* newTransaction)
66761efcc32SAxel Dörfler		:
66861efcc32SAxel Dörfler		fCache(cache),
66961efcc32SAxel Dörfler		fTransaction(transaction),
67061efcc32SAxel Dörfler		fID(transaction->id),
67161efcc32SAxel Dörfler		fSub(transaction->has_sub_transaction),
67261efcc32SAxel Dörfler		fNewTransaction(newTransaction),
67361efcc32SAxel Dörfler		fNewID(newTransaction->id)
67461efcc32SAxel Dörfler	{
67561efcc32SAxel Dörfler		Initialized();
67661efcc32SAxel Dörfler	}
6774fc4f2c8SAxel Dörfler
67861efcc32SAxel Dörfler	virtual void AddDump(TraceOutput& out)
67961efcc32SAxel Dörfler	{
6800e88a887SAlex Smith		out.Print("block cache %p, detach transaction %p (id %" B_PRId32 ")"
6810e88a887SAlex Smith			"from transaction %p (id %" B_PRId32 ")%s",
68261efcc32SAxel Dörfler			fCache, fNewTransaction, fNewID, fTransaction, fID,
68361efcc32SAxel Dörfler			fSub ? " sub" : "");
68461efcc32SAxel Dörfler	}
6854fc4f2c8SAxel Dörfler
68661efcc32SAxel Dörflerprivate:
68761efcc32SAxel Dörfler	block_cache*		fCache;
68861efcc32SAxel Dörfler	cache_transaction*	fTransaction;
68961efcc32SAxel Dörfler	int32				fID;
69061efcc32SAxel Dörfler	bool				fSub;
69161efcc32SAxel Dörfler	cache_transaction*	fNewTransaction;
69261efcc32SAxel Dörfler	int32				fNewID;
693502c4640SAxel Dörfler};
694502c4640SAxel Dörfler
695502c4640SAxel Dörflerclass Abort : public AbstractTraceEntry {
69661efcc32SAxel Dörflerpublic:
69761efcc32SAxel Dörfler	Abort(block_cache* cache, cache_transaction* transaction)
69861efcc32SAxel Dörfler		:
69961efcc32SAxel Dörfler		fCache(cache),
70061efcc32SAxel Dörfler		fTransaction(transaction),
70161efcc32SAxel Dörfler		fID(transaction->id),
70261efcc32SAxel Dörfler		fNumBlocks(0)
70361efcc32SAxel Dörfler	{
70461efcc32SAxel Dörfler		bool isSub = transaction->has_sub_transaction;
70561efcc32SAxel Dörfler		fNumBlocks = isSub ? transaction->sub_num_blocks
70661efcc32SAxel Dörfler			: transaction->num_blocks;
70761efcc32SAxel Dörfler		fBlocks = (off_t*)alloc_tracing_buffer(fNumBlocks * sizeof(off_t));
70861efcc32SAxel Dörfler		if (fBlocks != NULL) {
70961efcc32SAxel Dörfler			cached_block* block = transaction->first_block;
71061efcc32SAxel Dörfler			for (int32 i = 0; block != NULL && i < fNumBlocks;
71161efcc32SAxel Dörfler					block = block->transaction_next) {
71261efcc32SAxel Dörfler				fBlocks[i++] = block->block_number;
71361efcc32SAxel Dörfler			}
71461efcc32SAxel Dörfler		} else
71561efcc32SAxel Dörfler			fNumBlocks = 0;
7160a5bfde2SAxel Dörfler
7170a5bfde2SAxel Dörfler#if KTRACE_PRINTF_STACK_TRACE
7180a5bfde2SAxel Dörfler		fStackTrace = capture_tracing_stack_trace(KTRACE_PRINTF_STACK_TRACE, 1,
7190a5bfde2SAxel Dörfler			false);
7200a5bfde2SAxel Dörfler#endif
7210a5bfde2SAxel Dörfler
72261efcc32SAxel Dörfler		Initialized();
72361efcc32SAxel Dörfler	}
724502c4640SAxel Dörfler
72561efcc32SAxel Dörfler	virtual void AddDump(TraceOutput& out)
72661efcc32SAxel Dörfler	{
72761efcc32SAxel Dörfler		out.Print("block cache %p, abort transaction "
7280e88a887SAlex Smith			"%p (id %" B_PRId32 "), blocks", fCache, fTransaction, fID);
72961efcc32SAxel Dörfler		for (int32 i = 0; i < fNumBlocks && !out.IsFull(); i++)
7300e88a887SAlex Smith			out.Print(" %" B_PRIdOFF, fBlocks[i]);
73161efcc32SAxel Dörfler	}
732502c4640SAxel Dörfler
7330a5bfde2SAxel Dörfler#if KTRACE_PRINTF_STACK_TRACE
7340a5bfde2SAxel Dörfler	virtual void DumpStackTrace(TraceOutput& out)
7350a5bfde2SAxel Dörfler	{
7360a5bfde2SAxel Dörfler		out.PrintStackTrace(fStackTrace);
7370a5bfde2SAxel Dörfler	}
7380a5bfde2SAxel Dörfler#endif
7390a5bfde2SAxel Dörfler
74061efcc32SAxel Dörflerprivate:
74161efcc32SAxel Dörfler	block_cache*		fCache;
74261efcc32SAxel Dörfler	cache_transaction*	fTransaction;
74361efcc32SAxel Dörfler	int32				fID;
74461efcc32SAxel Dörfler	off_t*				fBlocks;
74561efcc32SAxel Dörfler	int32				fNumBlocks;
7460a5bfde2SAxel Dörfler#if KTRACE_PRINTF_STACK_TRACE
7470a5bfde2SAxel Dörfler	tracing_stack_trace* fStackTrace;
7480a5bfde2SAxel Dörfler#endif
7494fc4f2c8SAxel Dörfler};
7504fc4f2c8SAxel Dörfler
7514fc4f2c8SAxel Dörfler}	// namespace TransactionTracing
7524fc4f2c8SAxel Dörfler
7534fc4f2c8SAxel Dörfler#	define T(x) new(std::nothrow) TransactionTracing::x;
7544fc4f2c8SAxel Dörfler#else
7554fc4f2c8SAxel Dörfler#	define T(x) ;
7564fc4f2c8SAxel Dörfler#endif