xref: /haiku/src/system/kernel/cache/block_cache.cpp (revision fef6144999c2fa611f59ee6ffe6dd7999501385c)
1 /*
2  * Copyright 2004-2005, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 
7 #include "block_cache_private.h"
8 
9 #include <KernelExport.h>
10 #include <fs_cache.h>
11 
12 #include <block_cache.h>
13 #include <lock.h>
14 #include <util/kernel_cpp.h>
15 #include <util/DoublyLinkedList.h>
16 #include <util/AutoLock.h>
17 #include <util/khash.h>
18 
19 #include <unistd.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <errno.h>
23 
24 
25 // ToDo: this is a naive but growing implementation to test the API:
26 //	1) block reading/writing is not at all optimized for speed, it will
27 //	   just read and write single blocks.
28 //	2) the locking could be improved; getting a block should not need to
29 //	   wait for blocks to be written
30 //	3) dirty blocks are only written back if asked for
31 //	4) blocks are never removed yet
32 
33 //#define TRACE_BLOCK_CACHE
34 #ifdef TRACE_BLOCK_CACHE
35 #	define TRACE(x)	dprintf x
36 #else
37 #	define TRACE(x) ;
38 #endif
39 
40 
41 struct cache_transaction {
42 	cache_transaction *next;
43 	int32			id;
44 	int32			num_blocks;
45 	cached_block	*first_block;
46 	block_list		blocks;
47 	transaction_notification_hook notification_hook;
48 	void			*notification_data;
49 	bool			open;
50 };
51 
52 static status_t write_cached_block(block_cache *cache, cached_block *block, bool deleteTransaction = true);
53 
54 
55 //	#pragma mark - private transaction
56 
57 
58 static int
59 transaction_compare(void *_transaction, const void *_id)
60 {
61 	cache_transaction *transaction = (cache_transaction *)_transaction;
62 	const int32 *id = (const int32 *)_id;
63 
64 	return transaction->id - *id;
65 }
66 
67 
68 static uint32
69 transaction_hash(void *_transaction, const void *_id, uint32 range)
70 {
71 	cache_transaction *transaction = (cache_transaction *)_transaction;
72 	const int32 *id = (const int32 *)_id;
73 
74 	if (transaction != NULL)
75 		return transaction->id % range;
76 
77 	return (uint32)*id % range;
78 }
79 
80 
81 static void
82 delete_transaction(block_cache *cache, cache_transaction *transaction)
83 {
84 	hash_remove(cache->transaction_hash, transaction);
85 	if (cache->last_transaction == transaction)
86 		cache->last_transaction = NULL;
87 
88 	delete transaction;
89 }
90 
91 
92 static cache_transaction *
93 lookup_transaction(block_cache *cache, int32 id)
94 {
95 	return (cache_transaction *)hash_lookup(cache->transaction_hash, &id);
96 }
97 
98 
99 //	#pragma mark - private block_cache
100 
101 
102 /* static */
103 int
104 cached_block::Compare(void *_cacheEntry, const void *_block)
105 {
106 	cached_block *cacheEntry = (cached_block *)_cacheEntry;
107 	const off_t *block = (const off_t *)_block;
108 
109 	return cacheEntry->block_number - *block;
110 }
111 
112 
113 
114 /* static */
115 uint32
116 cached_block::Hash(void *_cacheEntry, const void *_block, uint32 range)
117 {
118 	cached_block *cacheEntry = (cached_block *)_cacheEntry;
119 	const off_t *block = (const off_t *)_block;
120 
121 	if (cacheEntry != NULL)
122 		return cacheEntry->block_number % range;
123 
124 	return (uint64)*block % range;
125 }
126 
127 
128 //	#pragma mark -
129 
130 
131 block_cache::block_cache(int _fd, off_t numBlocks, size_t blockSize)
132 	:
133 	hash(NULL),
134 	fd(_fd),
135 	max_blocks(numBlocks),
136 	block_size(blockSize),
137 	next_transaction_id(1),
138 	last_transaction(NULL),
139 	transaction_hash(NULL),
140 	ranges_hash(NULL),
141 	free_ranges(NULL)
142 {
143 	hash = hash_init(32, 0, &cached_block::Compare, &cached_block::Hash);
144 	if (hash == NULL)
145 		return;
146 
147 	transaction_hash = hash_init(16, 0, &transaction_compare, &::transaction_hash);
148 	if (transaction_hash == NULL)
149 		return;
150 
151 	ranges_hash = hash_init(16, 0, &block_range::Compare, &block_range::Hash);
152 	if (ranges_hash == NULL)
153 		return;
154 
155 	if (benaphore_init(&lock, "block cache") < B_OK)
156 		return;
157 
158 	chunk_size = max_c(blockSize, B_PAGE_SIZE);
159 	chunks_per_range = kBlockRangeSize / chunk_size;
160 	range_mask = (1UL << chunks_per_range) - 1;
161 	chunk_mask = (1UL << (chunk_size / blockSize)) - 1;
162 }
163 
164 
165 block_cache::~block_cache()
166 {
167 	benaphore_destroy(&lock);
168 
169 	hash_uninit(ranges_hash);
170 	hash_uninit(transaction_hash);
171 	hash_uninit(hash);
172 }
173 
174 
175 status_t
176 block_cache::InitCheck()
177 {
178 	if (lock.sem < B_OK)
179 		return lock.sem;
180 
181 	if (hash == NULL || transaction_hash == NULL || ranges_hash == NULL)
182 		return B_NO_MEMORY;
183 
184 	return B_OK;
185 }
186 
187 
188 block_range *
189 block_cache::GetFreeRange()
190 {
191 	if (free_ranges != NULL)
192 		return free_ranges;
193 
194 	// we need to allocate a new range
195 	block_range *range;
196 	if (block_range::NewBlockRange(this, &range) != B_OK) {
197 		// ToDo: free up space in existing ranges
198 		// We may also need to free ranges from other caches to get a free one
199 		// (if not, an active volume might have stolen all free ranges already)
200 		return NULL;
201 	}
202 
203 	hash_insert(ranges_hash, range);
204 	return range;
205 }
206 
207 
208 block_range *
209 block_cache::GetRange(void *address)
210 {
211 	return (block_range *)hash_lookup(ranges_hash, address);
212 }
213 
214 
215 void
216 block_cache::Free(void *address)
217 {
218 	if (address == NULL)
219 		return;
220 
221 	block_range *range = GetRange(address);
222 	ASSERT(range != NULL);
223 	range->Free(this, address);
224 }
225 
226 
227 void *
228 block_cache::Allocate()
229 {
230 	block_range *range = GetFreeRange();
231 	if (range == NULL)
232 		return NULL;
233 
234 	return range->Allocate(this);
235 }
236 
237 
238 void
239 block_cache::FreeBlock(cached_block *block)
240 {
241 	block_range *range = GetRange(block->data);
242 	ASSERT(range != NULL);
243 	range->Free(this, block);
244 
245 	Free(block->original);
246 #ifdef DEBUG_CHANGED
247 	Free(block->compare);
248 #endif
249 
250 	free(block);
251 }
252 
253 
254 cached_block *
255 block_cache::NewBlock(off_t blockNumber)
256 {
257 	cached_block *block = (cached_block *)malloc(sizeof(cached_block));
258 	if (block == NULL)
259 		return NULL;
260 
261 	block_range *range = GetFreeRange();
262 	if (range == NULL) {
263 		free(block);
264 		return NULL;
265 	}
266 
267 	range->Allocate(this, block);
268 
269 	block->block_number = blockNumber;
270 	block->lock = 0;
271 	block->transaction_next = NULL;
272 	block->transaction = block->previous_transaction = NULL;
273 	block->original = NULL;
274 	block->is_dirty = false;
275 #ifdef DEBUG_CHANGED
276 	block->compare = NULL;
277 #endif
278 
279 	hash_insert(hash, block);
280 
281 	return block;
282 }
283 
284 
285 #ifdef DEBUG_CHANGED
286 
287 #define DUMPED_BLOCK_SIZE 16
288 
289 void
290 dumpBlock(const char *buffer, int size, const char *prefix)
291 {
292 	int i;
293 
294 	for (i = 0; i < size;) {
295 		int start = i;
296 
297 		dprintf(prefix);
298 		for (; i < start+DUMPED_BLOCK_SIZE; i++) {
299 			if (!(i % 4))
300 				dprintf(" ");
301 
302 			if (i >= size)
303 				dprintf("  ");
304 			else
305 				dprintf("%02x", *(unsigned char *)(buffer + i));
306 		}
307 		dprintf("  ");
308 
309 		for (i = start; i < start + DUMPED_BLOCK_SIZE; i++) {
310 			if (i < size) {
311 				char c = buffer[i];
312 
313 				if (c < 30)
314 					dprintf(".");
315 				else
316 					dprintf("%c", c);
317 			} else
318 				break;
319 		}
320 		dprintf("\n");
321 	}
322 }
323 #endif
324 
325 
326 static void
327 put_cached_block(block_cache *cache, cached_block *block)
328 {
329 #ifdef DEBUG_CHANGED
330 	if (!block->is_dirty && block->compare != NULL && memcmp(block->data, block->compare, cache->block_size)) {
331 		dprintf("new block:\n");
332 		dumpBlock((const char *)block->data, 256, "  ");
333 		dprintf("unchanged block:\n");
334 		dumpBlock((const char *)block->compare, 256, "  ");
335 		write_cached_block(cache, block);
336 		panic("block_cache: supposed to be clean block was changed!\n");
337 
338 		cache->Free(block->compare);
339 		block->compare = NULL;
340 	}
341 #endif
342 
343 	if (--block->lock == 0)
344 		;
345 //		block->data = cache->allocator->Release(block->data);
346 }
347 
348 
349 static void
350 put_cached_block(block_cache *cache, off_t blockNumber)
351 {
352 	cached_block *block = (cached_block *)hash_lookup(cache->hash, &blockNumber);
353 	if (block != NULL)
354 		put_cached_block(cache, block);
355 }
356 
357 
358 static cached_block *
359 get_cached_block(block_cache *cache, off_t blockNumber, bool &allocated, bool readBlock = true)
360 {
361 	cached_block *block = (cached_block *)hash_lookup(cache->hash, &blockNumber);
362 	allocated = false;
363 
364 	if (block == NULL) {
365 		// read block into cache
366 		block = cache->NewBlock(blockNumber);
367 		if (block == NULL)
368 			return NULL;
369 
370 		allocated = true;
371 	} else {
372 /*
373 		if (block->lock == 0 && block->data != NULL) {
374 			// see if the old block can be resurrected
375 			block->data = cache->allocator->Acquire(block->data);
376 		}
377 
378 		if (block->data == NULL) {
379 			// there is no block yet, but we need one
380 			block->data = cache->allocator->Get();
381 			if (block->data == NULL)
382 				return NULL;
383 
384 			allocated = true;
385 		}
386 */
387 	}
388 
389 	if (allocated && readBlock) {
390 		int32 blockSize = cache->block_size;
391 
392 		if (read_pos(cache->fd, blockNumber * blockSize, block->data, blockSize) < blockSize) {
393 			cache->FreeBlock(block);
394 			return NULL;
395 		}
396 	}
397 
398 	block->lock++;
399 	return block;
400 }
401 
402 
403 static void *
404 get_writable_cached_block(block_cache *cache, off_t blockNumber, off_t base, off_t length,
405 	int32 transactionID, bool cleared)
406 {
407 	BenaphoreLocker locker(&cache->lock);
408 
409 	TRACE(("get_writable_cached_block(blockNumber = %Ld, transaction = %ld)\n", blockNumber, transactionID));
410 
411 	bool allocated;
412 	cached_block *block = get_cached_block(cache, blockNumber, allocated, !cleared);
413 	if (block == NULL)
414 		return NULL;
415 
416 	// if there is no transaction support, we just return the current block
417 	if (transactionID == -1) {
418 		if (cleared)
419 			memset(block->data, 0, cache->block_size);
420 
421 		block->is_dirty = true;
422 			// mark the block as dirty
423 
424 		return block->data;
425 	}
426 
427 	// ToDo: note, even if we panic, we should probably put the cached block
428 	//	back before we return
429 
430 	if (block->transaction != NULL && block->transaction->id != transactionID) {
431 		// ToDo: we have to wait here until the other transaction is done.
432 		//	Maybe we should even panic, since we can't prevent any deadlocks.
433 		panic("get_writable_cached_block(): asked to get busy writable block (transaction %ld)\n", block->transaction->id);
434 		return NULL;
435 	}
436 	if (block->transaction == NULL && transactionID != -1) {
437 		// get new transaction
438 		cache_transaction *transaction = lookup_transaction(cache, transactionID);
439 		if (transaction == NULL) {
440 			panic("get_writable_cached_block(): invalid transaction %ld!\n", transactionID);
441 			return NULL;
442 		}
443 		if (!transaction->open) {
444 			panic("get_writable_cached_block(): transaction already done!\n");
445 			return NULL;
446 		}
447 
448 		block->transaction = transaction;
449 
450 		// attach the block to the transaction block list
451 		block->transaction_next = transaction->first_block;
452 		transaction->first_block = block;
453 		transaction->num_blocks++;
454 	}
455 
456 	if (!(allocated && cleared) && block->original == NULL) {
457 		// we already have data, so we need to save it
458 		block->original = cache->Allocate();
459 		if (block->original == NULL) {
460 			put_cached_block(cache, block);
461 			return NULL;
462 		}
463 
464 		memcpy(block->original, block->data, cache->block_size);
465 	}
466 
467 	if (cleared)
468 		memset(block->data, 0, cache->block_size);
469 
470 	block->is_dirty = true;
471 
472 	return block->data;
473 }
474 
475 
476 static status_t
477 write_cached_block(block_cache *cache, cached_block *block, bool deleteTransaction)
478 {
479 	cache_transaction *previous = block->previous_transaction;
480 	int32 blockSize = cache->block_size;
481 
482 	void *data = previous && block->original ? block->original : block->data;
483 		// we first need to write back changes from previous transactions
484 
485 	TRACE(("write_cached_block(block %Ld)\n", block->block_number));
486 
487 	ssize_t written = write_pos(cache->fd, block->block_number * blockSize, data, blockSize);
488 
489 	if (written < blockSize) {
490 		dprintf("could not write back block %Ld (%s)\n", block->block_number, strerror(errno));
491 		return B_IO_ERROR;
492 	}
493 
494 	if (data == block->data)
495 		block->is_dirty = false;
496 
497 	if (previous != NULL) {
498 		previous->blocks.Remove(block);
499 		block->previous_transaction = NULL;
500 
501 		// Has the previous transation been finished with that write?
502 		if (--previous->num_blocks == 0) {
503 			TRACE(("cache transaction %ld finished!\n", previous->id));
504 
505 			if (previous->notification_hook != NULL)
506 				previous->notification_hook(previous->id, previous->notification_data);
507 
508 			if (deleteTransaction)
509 				delete_transaction(cache, previous);
510 		}
511 	}
512 
513 	return B_OK;
514 }
515 
516 
517 extern "C" status_t
518 block_cache_init(void)
519 {
520 	return init_block_allocator();
521 }
522 
523 
524 //	#pragma mark - public transaction
525 
526 
527 extern "C" int32
528 cache_start_transaction(void *_cache)
529 {
530 	block_cache *cache = (block_cache *)_cache;
531 
532 	if (cache->last_transaction && cache->last_transaction->open)
533 		panic("last transaction (%ld) still open!\n", cache->last_transaction->id);
534 
535 	cache_transaction *transaction = new cache_transaction;
536 	if (transaction == NULL)
537 		return B_NO_MEMORY;
538 
539 	transaction->id = atomic_add(&cache->next_transaction_id, 1);
540 	transaction->num_blocks = 0;
541 	transaction->first_block = NULL;
542 	transaction->notification_hook = NULL;
543 	transaction->notification_data = NULL;
544 	transaction->open = true;
545 	cache->last_transaction = transaction;
546 
547 	TRACE(("cache_transaction_start(): id %ld started\n", transaction->id));
548 
549 	BenaphoreLocker locker(&cache->lock);
550 	hash_insert(cache->transaction_hash, transaction);
551 
552 	return transaction->id;
553 }
554 
555 
556 extern "C" status_t
557 cache_sync_transaction(void *_cache, int32 id)
558 {
559 	block_cache *cache = (block_cache *)_cache;
560 	BenaphoreLocker locker(&cache->lock);
561 	status_t status = B_ENTRY_NOT_FOUND;
562 
563 	hash_iterator iterator;
564 	hash_open(cache->transaction_hash, &iterator);
565 
566 	cache_transaction *transaction;
567 	while ((transaction = (cache_transaction *)hash_next(cache->transaction_hash, &iterator)) != NULL) {
568 		// ToDo: fix hash interface to make this easier
569 
570 		if (transaction->id <= id && !transaction->open) {
571 			while (transaction->num_blocks > 0) {
572 				status = write_cached_block(cache, transaction->blocks.Head(), false);
573 				if (status != B_OK)
574 					return status;
575 			}
576 			delete_transaction(cache, transaction);
577 			hash_rewind(cache->transaction_hash, &iterator);
578 		}
579 	}
580 
581 	hash_close(cache->transaction_hash, &iterator, false);
582 	return B_OK;
583 }
584 
585 
586 extern "C" status_t
587 cache_end_transaction(void *_cache, int32 id, transaction_notification_hook hook, void *data)
588 {
589 	block_cache *cache = (block_cache *)_cache;
590 	BenaphoreLocker locker(&cache->lock);
591 
592 	TRACE(("cache_end_transaction(id = %ld)\n", id));
593 
594 	cache_transaction *transaction = lookup_transaction(cache, id);
595 	if (transaction == NULL) {
596 		panic("cache_end_transaction(): invalid transaction ID\n");
597 		return B_BAD_VALUE;
598 	}
599 
600 	transaction->notification_hook = hook;
601 	transaction->notification_data = data;
602 
603 	// iterate through all blocks and free the unchanged original contents
604 
605 	cached_block *block = transaction->first_block, *next;
606 	for (; block != NULL; block = next) {
607 		next = block->transaction_next;
608 
609 		if (block->previous_transaction != NULL) {
610 			// need to write back pending changes
611 			write_cached_block(cache, block);
612 		}
613 
614 		if (block->original != NULL) {
615 			cache->Free(block->original);
616 			block->original = NULL;
617 		}
618 
619 		// move the block to the previous transaction list
620 		transaction->blocks.Add(block);
621 
622 		block->previous_transaction = transaction;
623 		block->transaction_next = NULL;
624 		block->transaction = NULL;
625 	}
626 
627 	transaction->open = false;
628 
629 	return B_OK;
630 }
631 
632 
633 extern "C" status_t
634 cache_abort_transaction(void *_cache, int32 id)
635 {
636 	block_cache *cache = (block_cache *)_cache;
637 	BenaphoreLocker locker(&cache->lock);
638 
639 	TRACE(("cache_abort_transaction(id = %ld)\n", id));
640 
641 	cache_transaction *transaction = lookup_transaction(cache, id);
642 	if (transaction == NULL) {
643 		panic("cache_abort_transaction(): invalid transaction ID\n");
644 		return B_BAD_VALUE;
645 	}
646 
647 	// iterate through all blocks and restore their original contents
648 
649 	cached_block *block = transaction->first_block, *next;
650 	for (; block != NULL; block = next) {
651 		next = block->transaction_next;
652 
653 		if (block->original != NULL) {
654 			TRACE(("cache_abort_transaction(id = %ld): restored contents of block %Ld\n",
655 				transaction->id, block->block_number));
656 			memcpy(block->data, block->original, cache->block_size);
657 			cache->Free(block->original);
658 			block->original = NULL;
659 		}
660 
661 		block->transaction_next = NULL;
662 		block->transaction = NULL;
663 	}
664 
665 	delete_transaction(cache, transaction);
666 	return B_OK;
667 }
668 
669 
670 extern "C" int32
671 cache_detach_sub_transaction(void *_cache, int32 id)
672 {
673 	return B_ERROR;
674 }
675 
676 
677 extern "C" status_t
678 cache_abort_sub_transaction(void *_cache, int32 id)
679 {
680 	return B_ERROR;
681 }
682 
683 
684 extern "C" status_t
685 cache_start_sub_transaction(void *_cache, int32 id)
686 {
687 	return B_ERROR;
688 }
689 
690 
691 extern "C" status_t
692 cache_next_block_in_transaction(void *_cache, int32 id, uint32 *_cookie, off_t *_blockNumber,
693 	void **_data, void **_unchangedData)
694 {
695 	cached_block *block = (cached_block *)*_cookie;
696 	block_cache *cache = (block_cache *)_cache;
697 
698 	BenaphoreLocker locker(&cache->lock);
699 
700 	cache_transaction *transaction = lookup_transaction(cache, id);
701 	if (transaction == NULL)
702 		return B_BAD_VALUE;
703 
704 	if (block == NULL)
705 		block = transaction->first_block;
706 	else
707 		block = block->transaction_next;
708 
709 	if (block == NULL)
710 		return B_ENTRY_NOT_FOUND;
711 
712 	if (_blockNumber)
713 		*_blockNumber = block->block_number;
714 	if (_data)
715 		*_data = block->data;
716 	if (_unchangedData)
717 		*_unchangedData = block->original;
718 
719 	*_cookie = (uint32)block;
720 	return B_OK;
721 }
722 
723 
724 //	#pragma mark - public block cache
725 //	public interface
726 
727 
728 extern "C" void
729 block_cache_delete(void *_cache, bool allowWrites)
730 {
731 	block_cache *cache = (block_cache *)_cache;
732 
733 	if (allowWrites)
734 		block_cache_sync(cache);
735 
736 	// free all blocks
737 
738 	uint32 cookie = 0;
739 	cached_block *block;
740 	while ((block = (cached_block *)hash_remove_first(cache->hash, &cookie)) != NULL) {
741 		cache->FreeBlock(block);
742 	}
743 
744 	// free all transactions (they will all be aborted)
745 
746 	cookie = 0;
747 	cache_transaction *transaction;
748 	while ((transaction = (cache_transaction *)hash_remove_first(cache->transaction_hash, &cookie)) != NULL) {
749 		delete transaction;
750 	}
751 
752 	delete cache;
753 }
754 
755 
756 extern "C" void *
757 block_cache_create(int fd, off_t numBlocks, size_t blockSize)
758 {
759 	block_cache *cache = new block_cache(fd, numBlocks, blockSize);
760 	if (cache == NULL)
761 		return NULL;
762 
763 	if (cache->InitCheck() != B_OK) {
764 		delete cache;
765 		return NULL;
766 	}
767 
768 	return cache;
769 }
770 
771 
772 extern "C" status_t
773 block_cache_sync(void *_cache)
774 {
775 	block_cache *cache = (block_cache *)_cache;
776 
777 	// we will sync all dirty blocks to disk that have a completed
778 	// transaction or no transaction only
779 
780 	BenaphoreLocker locker(&cache->lock);
781 	hash_iterator iterator;
782 	hash_open(cache->hash, &iterator);
783 
784 	cached_block *block;
785 	while ((block = (cached_block *)hash_next(cache->hash, &iterator)) != NULL) {
786 		if (block->previous_transaction != NULL
787 			|| (block->transaction == NULL && block->is_dirty)) {
788 			status_t status = write_cached_block(cache, block);
789 			if (status != B_OK)
790 				return status;
791 		}
792 	}
793 
794 	hash_close(cache->hash, &iterator, false);
795 	return B_OK;
796 }
797 
798 
799 extern "C" status_t
800 block_cache_make_writable(void *_cache, off_t blockNumber, int32 transaction)
801 {
802 	// ToDo: this can be done better!
803 	void *block = block_cache_get_writable_etc(_cache, blockNumber, blockNumber, 1, transaction);
804 	if (block != NULL) {
805 		put_cached_block((block_cache *)_cache, blockNumber);
806 		return B_OK;
807 	}
808 
809 	return B_ERROR;
810 }
811 
812 
813 extern "C" void *
814 block_cache_get_writable_etc(void *_cache, off_t blockNumber, off_t base, off_t length,
815 	int32 transaction)
816 {
817 	TRACE(("block_cache_get_writable_etc(block = %Ld, transaction = %ld)\n", blockNumber, transaction));
818 
819 	return get_writable_cached_block((block_cache *)_cache, blockNumber,
820 				base, length, transaction, false);
821 }
822 
823 
824 extern "C" void *
825 block_cache_get_writable(void *_cache, off_t blockNumber, int32 transaction)
826 {
827 	return block_cache_get_writable_etc(_cache, blockNumber, blockNumber, 1, transaction);
828 }
829 
830 
831 extern "C" void *
832 block_cache_get_empty(void *_cache, off_t blockNumber, int32 transaction)
833 {
834 	TRACE(("block_cache_get_empty(block = %Ld, transaction = %ld)\n", blockNumber, transaction));
835 
836 	return get_writable_cached_block((block_cache *)_cache, blockNumber,
837 				blockNumber, 1, transaction, true);
838 }
839 
840 
841 extern "C" const void *
842 block_cache_get_etc(void *_cache, off_t blockNumber, off_t base, off_t length)
843 {
844 	block_cache *cache = (block_cache *)_cache;
845 	BenaphoreLocker locker(&cache->lock);
846 	bool allocated;
847 
848 	cached_block *block = get_cached_block(cache, blockNumber, allocated);
849 	if (block == NULL)
850 		return NULL;
851 
852 #ifdef DEBUG_CHANGED
853 	if (block->compare == NULL)
854 		block->compare = cache->Allocate();
855 	if (block->compare != NULL)
856 		memcpy(block->compare, block->data, cache->block_size);
857 #endif
858 	return block->data;
859 }
860 
861 
862 extern "C" const void *
863 block_cache_get(void *_cache, off_t blockNumber)
864 {
865 	return block_cache_get_etc(_cache, blockNumber, blockNumber, 1);
866 }
867 
868 
869 extern "C" status_t
870 block_cache_set_dirty(void *_cache, off_t blockNumber, bool isDirty, int32 transaction)
871 {
872 	// not yet implemented
873 	// Note, you must only use this function on blocks that were acquired writable!
874 	if (isDirty)
875 		panic("block_cache_set_dirty(): not yet implemented that way!\n");
876 
877 	return B_OK;
878 }
879 
880 
881 extern "C" void
882 block_cache_put(void *_cache, off_t blockNumber)
883 {
884 	block_cache *cache = (block_cache *)_cache;
885 	BenaphoreLocker locker(&cache->lock);
886 
887 	put_cached_block(cache, blockNumber);
888 }
889 
890