Ejemplo n.º 1
0
/*!	Removes a reference from the specified \a block. If this was the last
	reference, the block is moved into the unused list.
	In low memory situations, it will also free some blocks from that list,
	but not necessarily the \a block it just released.
*/
static void
put_cached_block(block_cache* cache, cached_block* block)
{
#ifdef DEBUG_CHANGED
	if (!block->is_dirty && block->compare != NULL
		&& memcmp(block->current_data, block->compare, cache->block_size)) {
		fssh_dprintf("new block:\n");
		fssh_dump_block((const char*)block->current_data, 256, "  ");
		fssh_dprintf("unchanged block:\n");
		fssh_dump_block((const char*)block->compare, 256, "  ");
		write_cached_block(cache, block);
		fssh_panic("block_cache: supposed to be clean block was changed!\n");

		cache->Free(block->compare);
		block->compare = NULL;
	}
#endif

	if (--block->ref_count == 0
		&& block->transaction == NULL && block->previous_transaction == NULL) {
		// This block is not used anymore, and not part of any transaction
		if (block->discard) {
			cache->RemoveBlock(block);
		} else {
			// put this block in the list of unused blocks
			block->unused = true;
			cache->unused_blocks.Add(block);
		}
	}

	if (cache->allocated_block_count > kMaxBlockCount) {
		cache->RemoveUnusedBlocks(INT32_MAX,
			cache->allocated_block_count - kMaxBlockCount);
	}
}
Ejemplo n.º 2
0
static void
put_cached_block(block_cache* cache, fssh_off_t blockNumber)
{
	if (blockNumber < 0 || blockNumber >= cache->max_blocks) {
		fssh_panic("put_cached_block: invalid block number %" FSSH_B_PRIdOFF
			" (max %" FSSH_B_PRIdOFF ")", blockNumber, cache->max_blocks - 1);
	}

	cached_block* block = (cached_block*)hash_lookup(cache->hash, &blockNumber);
	if (block != NULL)
		put_cached_block(cache, block);
}
Ejemplo n.º 3
0
/*!	Retrieves the block \a blockNumber from the hash table, if it's already
	there, or reads it from the disk.

	\param _allocated tells you whether or not a new block has been allocated
		to satisfy your request.
	\param readBlock if \c false, the block will not be read in case it was
		not already in the cache. The block you retrieve may contain random
		data.
*/
static cached_block*
get_cached_block(block_cache* cache, fssh_off_t blockNumber, bool* _allocated,
	bool readBlock = true)
{
	if (blockNumber < 0 || blockNumber >= cache->max_blocks) {
		fssh_panic("get_cached_block: invalid block number %" FSSH_B_PRIdOFF
			" (max %" FSSH_B_PRIdOFF ")", blockNumber, cache->max_blocks - 1);
		return NULL;
	}

	cached_block* block = (cached_block*)hash_lookup(cache->hash,
		&blockNumber);
	*_allocated = false;

	if (block == NULL) {
		// read block into cache
		block = cache->NewBlock(blockNumber);
		if (block == NULL)
			return NULL;

		hash_insert(cache->hash, block);
		*_allocated = true;
	}

	if (*_allocated && readBlock) {
		int32_t blockSize = cache->block_size;

		if (fssh_read_pos(cache->fd, blockNumber * blockSize, block->current_data,
				blockSize) < blockSize) {
			cache->RemoveBlock(block);
			FATAL(("could not read block %" FSSH_B_PRIdOFF "\n", blockNumber));
			return NULL;
		}
	}

	if (block->unused) {
		//TRACE(("remove block %Ld from unused\n", blockNumber));
		block->unused = false;
		cache->unused_blocks.Remove(block);
	}

	block->ref_count++;
	block->accessed++;

	return block;
}
Ejemplo n.º 4
0
void
block_cache::FreeBlock(cached_block* block)
{
	Free(block->current_data);

	if (block->original_data != NULL || block->parent_data != NULL) {
		fssh_panic("block_cache::FreeBlock(): %" FSSH_B_PRIdOFF
			", original %p, parent %p\n", block->block_number,
			block->original_data, block->parent_data);
	}

#ifdef DEBUG_CHANGED
	Free(block->compare);
#endif

	delete block;
}
Ejemplo n.º 5
0
static fssh_status_t
cache_io(void *_cacheRef, void *cookie, fssh_off_t offset, fssh_addr_t buffer,
         fssh_size_t *_size, bool doWrite)
{
    if (_cacheRef == NULL)
        fssh_panic("cache_io() called with NULL ref!\n");

    file_cache_ref *ref = (file_cache_ref *)_cacheRef;
    fssh_off_t fileSize = ref->virtual_size;

    TRACE(("cache_io(ref = %p, offset = %Ld, buffer = %p, size = %u, %s)\n",
           ref, offset, (void *)buffer, *_size, doWrite ? "write" : "read"));

    // out of bounds access?
    if (offset >= fileSize || offset < 0) {
        *_size = 0;
        return FSSH_B_OK;
    }

    int32_t pageOffset = offset & (FSSH_B_PAGE_SIZE - 1);
    fssh_size_t size = *_size;
    offset -= pageOffset;

    if ((uint64_t)offset + pageOffset + size > (uint64_t)fileSize) {
        // adapt size to be within the file's offsets
        size = fileSize - pageOffset - offset;
        *_size = size;
    }
    if (size == 0)
        return FSSH_B_OK;

    cache_func function;
    if (doWrite) {
        // in low memory situations, we bypass the cache beyond a
        // certain I/O size
        function = write_to_file;
    } else {
        function = read_from_file;
    }

    // "offset" and "lastOffset" are always aligned to B_PAGE_SIZE,
    // the "last*" variables always point to the end of the last
    // satisfied request part

    const uint32_t kMaxChunkSize = MAX_IO_VECS * FSSH_B_PAGE_SIZE;
    fssh_size_t bytesLeft = size, lastLeft = size;
    int32_t lastPageOffset = pageOffset;
    fssh_addr_t lastBuffer = buffer;
    fssh_off_t lastOffset = offset;

    MutexLocker locker(ref->lock);

    while (bytesLeft > 0) {
        // check if this page is already in memory
        fssh_size_t bytesInPage = fssh_min_c(
                                      fssh_size_t(FSSH_B_PAGE_SIZE - pageOffset), bytesLeft);

        if (bytesLeft <= bytesInPage)
            break;

        buffer += bytesInPage;
        bytesLeft -= bytesInPage;
        pageOffset = 0;
        offset += FSSH_B_PAGE_SIZE;

        if (buffer - lastBuffer + lastPageOffset >= kMaxChunkSize) {
            fssh_status_t status = satisfy_cache_io(ref, cookie, function,
                                                    offset, buffer, pageOffset, bytesLeft, lastOffset,
                                                    lastBuffer, lastPageOffset, lastLeft);
            if (status != FSSH_B_OK)
                return status;
        }
    }

    // fill the last remaining bytes of the request (either write or read)

    return function(ref, cookie, lastOffset, lastPageOffset, lastBuffer,
                    lastLeft);
}
Ejemplo n.º 6
0
void
fssh_kernel_debugger(const char *message)
{
    fssh_panic("%s", message);
}
Ejemplo n.º 7
0
/*!	Returns the writable block data for the requested blockNumber.
	If \a cleared is true, the block is not read from disk; an empty block
	is returned.

	This is the only method to insert a block into a transaction. It makes
	sure that the previous block contents are preserved in that case.
*/
static void*
get_writable_cached_block(block_cache* cache, fssh_off_t blockNumber, fssh_off_t base,
	fssh_off_t length, int32_t transactionID, bool cleared)
{
	TRACE(("get_writable_cached_block(blockNumber = %Ld, transaction = %d)\n",
		blockNumber, transactionID));

	if (blockNumber < 0 || blockNumber >= cache->max_blocks) {
		fssh_panic("get_writable_cached_block: invalid block number %"
			FSSH_B_PRIdOFF " (max %" FSSH_B_PRIdOFF ")", blockNumber,
			cache->max_blocks - 1);
	}

	bool allocated;
	cached_block* block = get_cached_block(cache, blockNumber, &allocated,
		!cleared);
	if (block == NULL)
		return NULL;

	block->discard = false;

	// if there is no transaction support, we just return the current block
	if (transactionID == -1) {
		if (cleared)
			fssh_memset(block->current_data, 0, cache->block_size);

		block->is_dirty = true;
			// mark the block as dirty

		return block->current_data;
	}

	cache_transaction* transaction = block->transaction;

	if (transaction != NULL && transaction->id != transactionID) {
		// TODO: we have to wait here until the other transaction is done.
		//	Maybe we should even panic, since we can't prevent any deadlocks.
		fssh_panic("get_writable_cached_block(): asked to get busy writable block (transaction %d)\n", (int)transaction->id);
		put_cached_block(cache, block);
		return NULL;
	}
	if (transaction == NULL && transactionID != -1) {
		// get new transaction
		transaction = lookup_transaction(cache, transactionID);
		if (transaction == NULL) {
			fssh_panic("get_writable_cached_block(): invalid transaction %d!\n",
				(int)transactionID);
			put_cached_block(cache, block);
			return NULL;
		}
		if (!transaction->open) {
			fssh_panic("get_writable_cached_block(): transaction already done!\n");
			put_cached_block(cache, block);
			return NULL;
		}

		block->transaction = transaction;

		// attach the block to the transaction block list
		block->transaction_next = transaction->first_block;
		transaction->first_block = block;
		transaction->num_blocks++;
	}

	bool wasUnchanged = block->original_data == NULL
		|| block->previous_transaction != NULL;

	if (!(allocated && cleared) && block->original_data == NULL) {
		// we already have data, so we need to preserve it
		block->original_data = cache->Allocate();
		if (block->original_data == NULL) {
			FATAL(("could not allocate original_data\n"));
			put_cached_block(cache, block);
			return NULL;
		}

		fssh_memcpy(block->original_data, block->current_data, cache->block_size);
	}
	if (block->parent_data == block->current_data) {
		// remember any previous contents for the parent transaction
		block->parent_data = cache->Allocate();
		if (block->parent_data == NULL) {
			// TODO: maybe we should just continue the current transaction in this case...
			FATAL(("could not allocate parent\n"));
			put_cached_block(cache, block);
			return NULL;
		}

		fssh_memcpy(block->parent_data, block->current_data, cache->block_size);
		transaction->sub_num_blocks++;
	} else if (transaction != NULL && transaction->has_sub_transaction
		&& block->parent_data == NULL && wasUnchanged)
		transaction->sub_num_blocks++;

	if (cleared)
		fssh_memset(block->current_data, 0, cache->block_size);

	block->is_dirty = true;

	return block->current_data;
}