mirror of
https://github.com/genodelabs/genode.git
synced 2025-06-17 06:38:28 +00:00
@ -650,7 +650,6 @@ set default_test_pkgs {
|
|||||||
test-spark_exception
|
test-spark_exception
|
||||||
test-spark_secondary_stack
|
test-spark_secondary_stack
|
||||||
test-block
|
test-block
|
||||||
test-block_cache
|
|
||||||
test-clipboard
|
test-clipboard
|
||||||
test-depot_query_index
|
test-depot_query_index
|
||||||
test-ds_ownership
|
test-ds_ownership
|
||||||
|
@ -6,8 +6,7 @@ Usage
|
|||||||
The plugin takes no configuration options.
|
The plugin takes no configuration options.
|
||||||
|
|
||||||
Caching
|
Caching
|
||||||
~~~~~~~~
|
~~~~~~~
|
||||||
|
|
||||||
This plugin may cache some file data but schedules a full write cache flush a few
|
This plugin may cache some file data but schedules a full write cache flush a
|
||||||
seconds after any write operation. If a read caching is desired, please use the
|
few seconds after any write operation.
|
||||||
'block_cache' component to cache at the block device.
|
|
||||||
|
@ -1 +0,0 @@
|
|||||||
Test of Block session interface provided by server/block_cache
|
|
@ -1,3 +0,0 @@
|
|||||||
_/src/init
|
|
||||||
_/src/test-block
|
|
||||||
_/src/block_cache
|
|
@ -1 +0,0 @@
|
|||||||
2022-01-18 402df20aabf69c4ef0a36901ff6fb26f6a1644c9
|
|
@ -1,54 +0,0 @@
|
|||||||
<runtime ram="32M" caps="1000" binary="init">
|
|
||||||
|
|
||||||
<requires> <timer/> </requires>
|
|
||||||
|
|
||||||
<events>
|
|
||||||
<timeout meaning="failed" sec="20" />
|
|
||||||
<log meaning="succeeded">--- all tests finished ---</log>
|
|
||||||
<log meaning="failed">test-block-client] Error: </log>
|
|
||||||
</events>
|
|
||||||
|
|
||||||
<content>
|
|
||||||
<rom label="ld.lib.so"/>
|
|
||||||
<rom label="test-block-client"/>
|
|
||||||
<rom label="test-block-server"/>
|
|
||||||
<rom label="block_cache"/>
|
|
||||||
</content>
|
|
||||||
|
|
||||||
<config>
|
|
||||||
<parent-provides>
|
|
||||||
<service name="ROM"/>
|
|
||||||
<service name="IRQ"/>
|
|
||||||
<service name="IO_MEM"/>
|
|
||||||
<service name="IO_PORT"/>
|
|
||||||
<service name="PD"/>
|
|
||||||
<service name="RM"/>
|
|
||||||
<service name="CPU"/>
|
|
||||||
<service name="LOG"/>
|
|
||||||
<service name="Timer"/>
|
|
||||||
</parent-provides>
|
|
||||||
<default-route>
|
|
||||||
<any-service> <parent/> <any-child/> </any-service>
|
|
||||||
</default-route>
|
|
||||||
<default caps="100"/>
|
|
||||||
<start name="test-block-server">
|
|
||||||
<resource name="RAM" quantum="10M"/>
|
|
||||||
<provides><service name="Block"/></provides>
|
|
||||||
</start>
|
|
||||||
<start name="block_cache">
|
|
||||||
<resource name="RAM" quantum="3M" />
|
|
||||||
<provides><service name="Block" /></provides>
|
|
||||||
<route>
|
|
||||||
<service name="Block"><child name="test-block-server" /></service>
|
|
||||||
<any-service> <parent /> <any-child /></any-service>
|
|
||||||
</route>
|
|
||||||
</start>
|
|
||||||
<start name="test-block-client">
|
|
||||||
<resource name="RAM" quantum="2G" />
|
|
||||||
<route>
|
|
||||||
<service name="Block"><child name="block_cache" /></service>
|
|
||||||
<any-service> <parent /> <any-child /></any-service>
|
|
||||||
</route>
|
|
||||||
</start>
|
|
||||||
</config>
|
|
||||||
</runtime>
|
|
@ -1,2 +0,0 @@
|
|||||||
SRC_DIR = src/server/block_cache
|
|
||||||
include $(GENODE_DIR)/repos/base/recipes/src/content.inc
|
|
@ -1 +0,0 @@
|
|||||||
2022-01-18 fadb7bcdf02aa959281f51a432f36525157492cb
|
|
@ -1,3 +0,0 @@
|
|||||||
base
|
|
||||||
os
|
|
||||||
block_session
|
|
@ -1,594 +0,0 @@
|
|||||||
/*
|
|
||||||
* \brief Data structure for storing sparse blocks
|
|
||||||
* \author Norman Feske
|
|
||||||
* \author Stefan Kalkowski
|
|
||||||
* \date 2014-01-06
|
|
||||||
*
|
|
||||||
* Note: originally taken from vfs/ram, and adapted to cache needs
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Copyright (C) 2014-2017 Genode Labs GmbH
|
|
||||||
*
|
|
||||||
* This file is part of the Genode OS framework, which is distributed
|
|
||||||
* under the terms of the GNU Affero General Public License version 3.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _CHUNK_H_
|
|
||||||
#define _CHUNK_H_
|
|
||||||
|
|
||||||
/* Genode includes */
|
|
||||||
#include <util/noncopyable.h>
|
|
||||||
#include <base/allocator.h>
|
|
||||||
#include <base/exception.h>
|
|
||||||
#include <util/list.h>
|
|
||||||
#include <util/string.h>
|
|
||||||
|
|
||||||
namespace Cache {
|
|
||||||
|
|
||||||
typedef Genode::uint64_t offset_t;
|
|
||||||
typedef Genode::uint64_t size_t;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Common base class of both 'Chunk' and 'Chunk_index'
|
|
||||||
*/
|
|
||||||
class Chunk_base : Genode::Noncopyable
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Noncopyable
|
|
||||||
*/
|
|
||||||
Chunk_base(Chunk_base const &);
|
|
||||||
Chunk_base &operator = (Chunk_base const &);
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
struct Range_exception : Genode::Exception
|
|
||||||
{
|
|
||||||
offset_t off;
|
|
||||||
size_t size;
|
|
||||||
|
|
||||||
Range_exception(offset_t o, size_t s) : off(o), size(s) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef Range_exception Index_out_of_range;
|
|
||||||
typedef Range_exception Range_incomplete;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
|
|
||||||
offset_t const _base_offset;
|
|
||||||
size_t _num_entries; /* corresponds to last used entry */
|
|
||||||
Chunk_base *_parent;
|
|
||||||
bool const _zero; /* marks zero chunk */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Test if specified range lies within the chunk
|
|
||||||
*/
|
|
||||||
void assert_valid_range(offset_t start, size_t len,
|
|
||||||
size_t chunk_size) const
|
|
||||||
{
|
|
||||||
if (start < _base_offset)
|
|
||||||
throw Index_out_of_range(start, len);
|
|
||||||
|
|
||||||
if (start + len > _base_offset + chunk_size)
|
|
||||||
throw Index_out_of_range(start, len);
|
|
||||||
}
|
|
||||||
|
|
||||||
Chunk_base(offset_t base_offset, Chunk_base *p)
|
|
||||||
: _base_offset(base_offset), _num_entries(0), _parent(p),
|
|
||||||
_zero(false) { }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Construct zero chunk
|
|
||||||
*/
|
|
||||||
Chunk_base()
|
|
||||||
: _base_offset(0), _num_entries(0), _parent(0), _zero(true) { }
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
virtual ~Chunk_base() { }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return absolute base offset of chunk in bytes
|
|
||||||
*/
|
|
||||||
offset_t base_offset() const { return _base_offset; }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return true if chunk has no allocated sub chunks
|
|
||||||
*/
|
|
||||||
bool empty() const { return _num_entries == 0; }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return true if this is an unused 'zero' chunk
|
|
||||||
*/
|
|
||||||
bool zero() const { return _zero; }
|
|
||||||
|
|
||||||
virtual void free(size_t, offset_t) = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Chunk of bytes used as leaf in hierarchy of chunk indices
|
|
||||||
*/
|
|
||||||
template <unsigned CHUNK_SIZE, typename POLICY>
|
|
||||||
class Chunk : public Chunk_base,
|
|
||||||
public POLICY::Element
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
|
|
||||||
char _data[CHUNK_SIZE];
|
|
||||||
unsigned _writes;
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
typedef Range_exception Dirty_chunk;
|
|
||||||
|
|
||||||
static constexpr size_t SIZE = CHUNK_SIZE;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Construct byte chunk
|
|
||||||
*
|
|
||||||
* \param base_offset absolute offset of chunk in bytes
|
|
||||||
*
|
|
||||||
* The first argument is unused. Its mere purpose is to make the
|
|
||||||
* signature of the constructor compatible to the constructor
|
|
||||||
* of 'Chunk_index'.
|
|
||||||
*/
|
|
||||||
Chunk(Genode::Allocator &, offset_t base_offset, Chunk_base *p)
|
|
||||||
: Chunk_base(base_offset, p), _writes(0) { }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Construct zero chunk
|
|
||||||
*/
|
|
||||||
Chunk() : _writes(0) { }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return number of used entries
|
|
||||||
*
|
|
||||||
* The returned value corresponds to the index of the last used
|
|
||||||
* entry + 1. It does not correlate to the number of actually
|
|
||||||
* allocated entries (there may be ranges of zero blocks).
|
|
||||||
*/
|
|
||||||
size_t used_size() const { return _num_entries; }
|
|
||||||
|
|
||||||
void write(char const *src, size_t len, offset_t seek_offset)
|
|
||||||
{
|
|
||||||
assert_valid_range(seek_offset, len, SIZE);
|
|
||||||
|
|
||||||
POLICY::write(this);
|
|
||||||
|
|
||||||
/* offset relative to this chunk */
|
|
||||||
offset_t const local_offset = seek_offset - base_offset();
|
|
||||||
|
|
||||||
Genode::memcpy(&_data[local_offset], src, len);
|
|
||||||
|
|
||||||
_num_entries = Genode::max(_num_entries, local_offset + len);
|
|
||||||
|
|
||||||
_writes++;
|
|
||||||
}
|
|
||||||
|
|
||||||
void read(char *dst, size_t len, offset_t seek_offset) const
|
|
||||||
{
|
|
||||||
assert_valid_range(seek_offset, len, SIZE);
|
|
||||||
|
|
||||||
POLICY::read(this);
|
|
||||||
|
|
||||||
Genode::memcpy(dst, &_data[seek_offset - base_offset()], len);
|
|
||||||
}
|
|
||||||
|
|
||||||
void stat(size_t len, offset_t seek_offset) const
|
|
||||||
{
|
|
||||||
assert_valid_range(seek_offset, len, SIZE);
|
|
||||||
|
|
||||||
if (_writes == 0)
|
|
||||||
throw Range_incomplete(base_offset(), SIZE);
|
|
||||||
}
|
|
||||||
|
|
||||||
void sync(size_t len, offset_t seek_offset)
|
|
||||||
{
|
|
||||||
if (_writes > 1) {
|
|
||||||
POLICY::sync(this, (char*)_data);
|
|
||||||
_writes = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void alloc(size_t len, offset_t seek_offset) { }
|
|
||||||
|
|
||||||
void truncate(size_t size)
|
|
||||||
{
|
|
||||||
assert_valid_range(size, 0, SIZE);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Offset of the first free position (relative to the beginning
|
|
||||||
* this chunk).
|
|
||||||
*/
|
|
||||||
offset_t const local_offset = size - base_offset();
|
|
||||||
|
|
||||||
if (local_offset >= _num_entries)
|
|
||||||
return;
|
|
||||||
|
|
||||||
Genode::memset(&_data[local_offset], 0, _num_entries - local_offset);
|
|
||||||
|
|
||||||
_num_entries = local_offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
void free(size_t, offset_t)
|
|
||||||
{
|
|
||||||
if (_writes > 1) throw Dirty_chunk(_base_offset, SIZE);
|
|
||||||
|
|
||||||
_num_entries = 0;
|
|
||||||
if (_parent) _parent->free(SIZE, _base_offset);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
template <unsigned NUM_ENTRIES, typename ENTRY_TYPE, typename POLICY>
|
|
||||||
class Chunk_index : public Chunk_base
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
typedef ENTRY_TYPE Entry;
|
|
||||||
|
|
||||||
static constexpr size_t ENTRY_SIZE = ENTRY_TYPE::SIZE;
|
|
||||||
static constexpr size_t SIZE = ENTRY_SIZE*NUM_ENTRIES;
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Noncopyable
|
|
||||||
*/
|
|
||||||
Chunk_index(Chunk_index const &);
|
|
||||||
Chunk_index &operator = (Chunk_index const &);
|
|
||||||
|
|
||||||
Genode::Allocator &_alloc;
|
|
||||||
|
|
||||||
Entry * _entries[NUM_ENTRIES];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return instance of a zero sub chunk
|
|
||||||
*/
|
|
||||||
static Entry &_zero_chunk()
|
|
||||||
{
|
|
||||||
static Entry zero_chunk;
|
|
||||||
return zero_chunk;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return sub chunk at given index
|
|
||||||
*
|
|
||||||
* If there is no sub chunk at the specified index, this function
|
|
||||||
* transparently allocates one. Hence, the returned sub chunk
|
|
||||||
* is ready to be written to.
|
|
||||||
*/
|
|
||||||
Entry &_alloc_entry(unsigned index)
|
|
||||||
{
|
|
||||||
if (index >= NUM_ENTRIES)
|
|
||||||
throw Index_out_of_range(base_offset() + index*ENTRY_SIZE,
|
|
||||||
ENTRY_SIZE);
|
|
||||||
|
|
||||||
if (_entries[index])
|
|
||||||
return *_entries[index];
|
|
||||||
|
|
||||||
offset_t entry_offset = base_offset() + index*ENTRY_SIZE;
|
|
||||||
|
|
||||||
for (;;) {
|
|
||||||
try {
|
|
||||||
_entries[index] = new (&_alloc)
|
|
||||||
Entry(_alloc, entry_offset, this);
|
|
||||||
break;
|
|
||||||
} catch(Genode::Allocator::Out_of_memory) {
|
|
||||||
POLICY::flush(sizeof(Entry));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_num_entries = Genode::max(_num_entries, index + 1);
|
|
||||||
|
|
||||||
return *_entries[index];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return sub chunk at given index
|
|
||||||
*/
|
|
||||||
Entry &_entry(unsigned index) const
|
|
||||||
{
|
|
||||||
if (index >= NUM_ENTRIES)
|
|
||||||
throw Index_out_of_range(base_offset() + index*ENTRY_SIZE,
|
|
||||||
ENTRY_SIZE);
|
|
||||||
|
|
||||||
if (_entries[index])
|
|
||||||
return *_entries[index];
|
|
||||||
|
|
||||||
throw Range_incomplete(base_offset() + index*ENTRY_SIZE,
|
|
||||||
ENTRY_SIZE);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return sub chunk at given index (for syncing only)
|
|
||||||
*
|
|
||||||
* This function transparently provides a zero sub chunk for any
|
|
||||||
* index that is not populated by a real chunk.
|
|
||||||
*/
|
|
||||||
Entry &_entry_for_syncing(unsigned index) const
|
|
||||||
{
|
|
||||||
if (index >= NUM_ENTRIES)
|
|
||||||
throw Index_out_of_range(base_offset() + index*ENTRY_SIZE,
|
|
||||||
ENTRY_SIZE);
|
|
||||||
|
|
||||||
if (_entries[index])
|
|
||||||
return *_entries[index];
|
|
||||||
|
|
||||||
return _zero_chunk();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return index of entry located at specified byte offset
|
|
||||||
*
|
|
||||||
* The caller of this function must make sure that the offset
|
|
||||||
* parameter is within the bounds of the chunk.
|
|
||||||
*/
|
|
||||||
unsigned _index_by_offset(offset_t offset) const
|
|
||||||
{
|
|
||||||
return (offset - base_offset()) / ENTRY_SIZE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Apply operation 'func' to a range of entries
|
|
||||||
*/
|
|
||||||
template <typename THIS, typename DATA, typename FUNC>
|
|
||||||
static void _range_op(THIS &obj, DATA *data, size_t len,
|
|
||||||
offset_t seek_offset, FUNC const &func)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* Depending on whether this function is called for reading
|
|
||||||
* (const function) or writing (non-const function), the
|
|
||||||
* operand type is const or non-const Entry. The correct type
|
|
||||||
* is embedded as a trait in the 'FUNC' functor type.
|
|
||||||
*/
|
|
||||||
typedef typename FUNC::Entry Const_qualified_entry;
|
|
||||||
|
|
||||||
obj.assert_valid_range(seek_offset, len, SIZE);
|
|
||||||
|
|
||||||
while (len > 0) {
|
|
||||||
|
|
||||||
unsigned const index = obj._index_by_offset(seek_offset);
|
|
||||||
|
|
||||||
Const_qualified_entry &entry = FUNC::lookup(obj, index);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Calculate byte offset relative to the chunk
|
|
||||||
*
|
|
||||||
* We cannot use 'entry.base_offset()' for this calculation
|
|
||||||
* because in the const case, the lookup might return a
|
|
||||||
* zero chunk, which has no defined base offset. Therefore,
|
|
||||||
* we calculate the base offset via index*ENTRY_SIZE.
|
|
||||||
*/
|
|
||||||
offset_t const local_seek_offset =
|
|
||||||
seek_offset - obj.base_offset() - index*ENTRY_SIZE;
|
|
||||||
|
|
||||||
/* available capacity at 'entry' starting at seek offset */
|
|
||||||
offset_t const capacity = ENTRY_SIZE - local_seek_offset;
|
|
||||||
size_t const curr_len = Genode::min(len, capacity);
|
|
||||||
|
|
||||||
/* apply functor (read or write) to entry */
|
|
||||||
func(entry, data, curr_len, seek_offset);
|
|
||||||
|
|
||||||
/* advance to next entry */
|
|
||||||
len -= curr_len;
|
|
||||||
data += curr_len;
|
|
||||||
seek_offset += curr_len;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Alloc_func
|
|
||||||
{
|
|
||||||
typedef ENTRY_TYPE Entry;
|
|
||||||
|
|
||||||
static Entry &lookup(Chunk_index &chunk, unsigned i) {
|
|
||||||
return chunk._alloc_entry(i); }
|
|
||||||
|
|
||||||
void operator () (Entry &entry, char const *src, size_t len,
|
|
||||||
offset_t seek_offset) const
|
|
||||||
{
|
|
||||||
entry.alloc(len, seek_offset);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Write_func
|
|
||||||
{
|
|
||||||
typedef ENTRY_TYPE Entry;
|
|
||||||
|
|
||||||
static Entry &lookup(Chunk_index &chunk, unsigned i) {
|
|
||||||
return chunk._entry(i); }
|
|
||||||
|
|
||||||
void operator () (Entry &entry, char const *src, size_t len,
|
|
||||||
offset_t seek_offset) const
|
|
||||||
{
|
|
||||||
entry.write(src, len, seek_offset);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Read_func
|
|
||||||
{
|
|
||||||
typedef ENTRY_TYPE const Entry;
|
|
||||||
|
|
||||||
static Entry &lookup(Chunk_index const &chunk, unsigned i) {
|
|
||||||
return chunk._entry(i); }
|
|
||||||
|
|
||||||
void operator () (Entry &entry, char *dst, size_t len,
|
|
||||||
offset_t seek_offset) const {
|
|
||||||
entry.read(dst, len, seek_offset); }
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Stat_func
|
|
||||||
{
|
|
||||||
typedef ENTRY_TYPE const Entry;
|
|
||||||
|
|
||||||
static Entry &lookup(Chunk_index const &chunk, unsigned i) {
|
|
||||||
return chunk._entry(i); }
|
|
||||||
|
|
||||||
void operator () (Entry &entry, char*, size_t len,
|
|
||||||
offset_t seek_offset) const {
|
|
||||||
entry.stat(len, seek_offset); }
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Sync_func
|
|
||||||
{
|
|
||||||
typedef ENTRY_TYPE Entry;
|
|
||||||
|
|
||||||
static Entry &lookup(Chunk_index const &chunk, unsigned i) {
|
|
||||||
return chunk._entry_for_syncing(i); }
|
|
||||||
|
|
||||||
void operator () (Entry &entry, char*, size_t len,
|
|
||||||
offset_t seek_offset) const
|
|
||||||
{
|
|
||||||
entry.sync(len, seek_offset);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
void _init_entries()
|
|
||||||
{
|
|
||||||
for (unsigned i = 0; i < NUM_ENTRIES; i++)
|
|
||||||
_entries[i] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void _destroy_entry(unsigned i)
|
|
||||||
{
|
|
||||||
if (_entries[i] && (i < _num_entries)) {
|
|
||||||
Genode::destroy(&_alloc, _entries[i]);
|
|
||||||
_entries[i] = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor
|
|
||||||
*
|
|
||||||
* \param alloc allocator to use for allocating sub-chunk
|
|
||||||
* indices and chunks
|
|
||||||
* \param base_offset absolute offset of the chunk in bytes
|
|
||||||
*/
|
|
||||||
Chunk_index(Genode::Allocator &alloc, offset_t base_offset,
|
|
||||||
Chunk_base *p = 0)
|
|
||||||
: Chunk_base(base_offset, p), _alloc(alloc) { _init_entries(); }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Construct zero chunk
|
|
||||||
*/
|
|
||||||
Chunk_index() : _alloc(*(Genode::Allocator *)0) { }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Destructor
|
|
||||||
*/
|
|
||||||
~Chunk_index()
|
|
||||||
{
|
|
||||||
for (unsigned i = 0; i < NUM_ENTRIES; i++)
|
|
||||||
_destroy_entry(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return size of chunk in bytes
|
|
||||||
*
|
|
||||||
* The returned value corresponds to the position after the highest
|
|
||||||
* offset that was written to.
|
|
||||||
*/
|
|
||||||
size_t used_size() const
|
|
||||||
{
|
|
||||||
if (_num_entries == 0)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
/* size of entries that lie completely within the used range */
|
|
||||||
size_t const size_whole_entries = ENTRY_SIZE*(_num_entries - 1);
|
|
||||||
|
|
||||||
Entry *last_entry = _entries[_num_entries - 1];
|
|
||||||
if (!last_entry)
|
|
||||||
return size_whole_entries;
|
|
||||||
|
|
||||||
return size_whole_entries + last_entry->used_size();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Write data to chunk
|
|
||||||
*/
|
|
||||||
void write(char const *src, size_t len, offset_t seek_offset) {
|
|
||||||
_range_op(*this, src, len, seek_offset, Write_func()); }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Allocate needed chunks
|
|
||||||
*/
|
|
||||||
void alloc(size_t len, offset_t seek_offset) {
|
|
||||||
_range_op(*this, (char*)0, len, seek_offset, Alloc_func()); }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Read data from chunk
|
|
||||||
*/
|
|
||||||
void read(char *dst, size_t len, offset_t seek_offset) const {
|
|
||||||
_range_op(*this, dst, len, seek_offset, Read_func()); }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check for chunk availability
|
|
||||||
*/
|
|
||||||
void stat(size_t len, offset_t seek_offset) const {
|
|
||||||
_range_op(*this, (char*)0, len, seek_offset, Stat_func()); }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Synchronize chunk when dirty
|
|
||||||
*/
|
|
||||||
void sync(size_t len, offset_t seek_offset) const {
|
|
||||||
if (zero()) return;
|
|
||||||
_range_op(*this, (char*)0, len, seek_offset, Sync_func()); }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Free chunks
|
|
||||||
*/
|
|
||||||
void free(size_t len, offset_t seek_offset)
|
|
||||||
{
|
|
||||||
unsigned const index = _index_by_offset(seek_offset);
|
|
||||||
Genode::destroy(&_alloc, _entries[index]);
|
|
||||||
_entries[index] = 0;
|
|
||||||
for (unsigned i = 0; i < NUM_ENTRIES; i++)
|
|
||||||
if (_entries[i])
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (_parent) _parent->free(SIZE, _base_offset);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Truncate chunk to specified size in bytes
|
|
||||||
*
|
|
||||||
* This function can be used to shrink a chunk only. Specifying a
|
|
||||||
* 'size' larger than 'used_size' has no effect. The value returned
|
|
||||||
* by 'used_size' refers always to the position of the last byte
|
|
||||||
* written to the chunk.
|
|
||||||
*/
|
|
||||||
void truncate(size_t size)
|
|
||||||
{
|
|
||||||
unsigned const trunc_index = _index_by_offset(size);
|
|
||||||
|
|
||||||
if (trunc_index >= _num_entries)
|
|
||||||
return;
|
|
||||||
|
|
||||||
for (unsigned i = trunc_index + 1; i < _num_entries; i++)
|
|
||||||
_destroy_entry(i);
|
|
||||||
|
|
||||||
/* traverse into sub chunks */
|
|
||||||
if (_entries[trunc_index])
|
|
||||||
_entries[trunc_index]->truncate(size);
|
|
||||||
|
|
||||||
_num_entries = trunc_index + 1;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* If the truncated at a chunk boundary, we can release the
|
|
||||||
* empty trailing chunk at 'trunc_index'.
|
|
||||||
*/
|
|
||||||
if (_entries[trunc_index] && _entries[trunc_index]->empty()) {
|
|
||||||
_destroy_entry(trunc_index);
|
|
||||||
_num_entries--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif /* _CHUNK_H_ */
|
|
@ -1,421 +0,0 @@
|
|||||||
/*
|
|
||||||
* \brief Cache driver
|
|
||||||
* \author Stefan Kalkowski
|
|
||||||
* \date 2013-12-05
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Copyright (C) 2013-2017 Genode Labs GmbH
|
|
||||||
*
|
|
||||||
* This file is part of the Genode OS framework, which is distributed
|
|
||||||
* under the terms of the GNU Affero General Public License version 3.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <base/log.h>
|
|
||||||
#include <block_session/connection.h>
|
|
||||||
#include <block/component.h>
|
|
||||||
#include <os/packet_allocator.h>
|
|
||||||
|
|
||||||
#include "chunk.h"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Cache driver used by the generic block driver framework
|
|
||||||
*
|
|
||||||
* \param POLICY the cache replacement policy (e.g. LRU)
|
|
||||||
*/
|
|
||||||
template <typename POLICY>
|
|
||||||
class Driver : public Block::Driver
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This class encapsulates requests to the backend device in progress,
|
|
||||||
* and the packets from the client side that triggered the request.
|
|
||||||
*/
|
|
||||||
struct Request : public Genode::List<Request>::Element
|
|
||||||
{
|
|
||||||
Block::Packet_descriptor srv;
|
|
||||||
Block::Packet_descriptor cli;
|
|
||||||
char * const buffer;
|
|
||||||
|
|
||||||
Request(Block::Packet_descriptor &s,
|
|
||||||
Block::Packet_descriptor &c,
|
|
||||||
char * const b)
|
|
||||||
: srv(s), cli(c), buffer(b) {}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* \return true when the given response packet matches
|
|
||||||
* the request send to the backend device
|
|
||||||
*/
|
|
||||||
bool match(const Block::Packet_descriptor& reply) const
|
|
||||||
{
|
|
||||||
return reply.operation() == srv.operation() &&
|
|
||||||
reply.block_number() == srv.block_number() &&
|
|
||||||
reply.block_count() == srv.block_count();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* \param write whether it's a write or read request
|
|
||||||
* \param nr block number requested
|
|
||||||
* \param cnt number of blocks requested
|
|
||||||
* \return true when the given parameters match
|
|
||||||
* the request send to the backend device
|
|
||||||
*/
|
|
||||||
bool match(const bool write,
|
|
||||||
const Block::sector_t nr,
|
|
||||||
const Genode::size_t cnt) const
|
|
||||||
{
|
|
||||||
Block::Packet_descriptor::Opcode op = write
|
|
||||||
? Block::Packet_descriptor::WRITE
|
|
||||||
: Block::Packet_descriptor::READ;
|
|
||||||
return op == srv.operation() &&
|
|
||||||
nr >= srv.block_number() &&
|
|
||||||
nr+cnt <= srv.block_number()+srv.block_count();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Write failed exception at a specific device offset,
|
|
||||||
* can be triggered whenever the backend device is not ready
|
|
||||||
* to proceed
|
|
||||||
*/
|
|
||||||
struct Write_failed : Genode::Exception
|
|
||||||
{
|
|
||||||
Cache::offset_t off;
|
|
||||||
|
|
||||||
Write_failed(Cache::offset_t o) : off(o) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The given policy class is extended by a synchronization routine,
|
|
||||||
* used by the cache chunk structure
|
|
||||||
*/
|
|
||||||
struct Policy : POLICY {
|
|
||||||
static void sync(const typename POLICY::Element *e, char *src); };
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
enum {
|
|
||||||
SLAB_SZ = Block::Session::TX_QUEUE_SIZE*sizeof(Request),
|
|
||||||
CACHE_BLK_SIZE = 4096
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* We use five levels of page-table like chunk structure,
|
|
||||||
* thereby we've a maximum device size of 256^4*4096 (LBA48)
|
|
||||||
*/
|
|
||||||
typedef Cache::Chunk<CACHE_BLK_SIZE, Policy> Chunk_level_4;
|
|
||||||
typedef Cache::Chunk_index<256, Chunk_level_4, Policy> Chunk_level_3;
|
|
||||||
typedef Cache::Chunk_index<256, Chunk_level_3, Policy> Chunk_level_2;
|
|
||||||
typedef Cache::Chunk_index<256, Chunk_level_2, Policy> Chunk_level_1;
|
|
||||||
typedef Cache::Chunk_index<256, Chunk_level_1, Policy> Chunk_level_0;
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
Genode::Env &_env;
|
|
||||||
Genode::Tslab<Request, SLAB_SZ> _r_slab; /* slab for requests */
|
|
||||||
Genode::List<Request> _r_list; /* list of requests */
|
|
||||||
Genode::Packet_allocator _alloc; /* packet allocator */
|
|
||||||
Block::Connection<> _blk; /* backend device */
|
|
||||||
Block::Session::Info const _info; /* block-device info */
|
|
||||||
Chunk_level_0 _cache; /* chunk hierarchy */
|
|
||||||
Genode::Io_signal_handler<Driver> _source_ack;
|
|
||||||
Genode::Io_signal_handler<Driver> _source_submit;
|
|
||||||
Genode::Io_signal_handler<Driver> _yield;
|
|
||||||
|
|
||||||
Driver(Driver const&); /* singleton pattern */
|
|
||||||
Driver& operator=(Driver const&); /* singleton pattern */
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Return modulus of cache's versus backend device's block size
|
|
||||||
*/
|
|
||||||
inline int _cache_blk_mod() { return CACHE_BLK_SIZE / _info.block_size; }
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Round off given block number to cache block size granularity
|
|
||||||
*/
|
|
||||||
inline Block::sector_t _cache_blk_round_off(Block::sector_t nr) {
|
|
||||||
return nr - (nr % _cache_blk_mod()); }
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Round up given block number to cache block size granularity
|
|
||||||
*/
|
|
||||||
inline Block::sector_t _cache_blk_round_up(Block::sector_t nr) {
|
|
||||||
return (nr % _cache_blk_mod())
|
|
||||||
? nr + _cache_blk_mod() - (nr % _cache_blk_mod())
|
|
||||||
: nr; }
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Handle response to a single request
|
|
||||||
*
|
|
||||||
* \param srv packet received from the backend device
|
|
||||||
* \param r outstanding request
|
|
||||||
*/
|
|
||||||
inline void _handle_reply(Block::Packet_descriptor &srv, Request *r)
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
if (r->cli.operation() == Block::Packet_descriptor::READ)
|
|
||||||
read(r->cli.block_number(), r->cli.block_count(),
|
|
||||||
r->buffer, r->cli);
|
|
||||||
else
|
|
||||||
write(r->cli.block_number(), r->cli.block_count(),
|
|
||||||
r->buffer, r->cli);
|
|
||||||
} catch(Block::Driver::Request_congestion) {
|
|
||||||
Genode::warning("cli (", r->cli.block_number(), " ",
|
|
||||||
r->cli.block_count(), ") "
|
|
||||||
"srv (", r->srv.block_number(), " ",
|
|
||||||
r->srv.block_count(), ")");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Handle acknowledgements from the backend device
|
|
||||||
*/
|
|
||||||
void _ack_avail()
|
|
||||||
{
|
|
||||||
while (_blk.tx()->ack_avail()) {
|
|
||||||
Block::Packet_descriptor p = _blk.tx()->get_acked_packet();
|
|
||||||
|
|
||||||
/* when reading, write result into cache */
|
|
||||||
if (p.operation() == Block::Packet_descriptor::READ)
|
|
||||||
_cache.write(_blk.tx()->packet_content(p),
|
|
||||||
p.block_count() * _info.block_size,
|
|
||||||
p.block_number() * _info.block_size);
|
|
||||||
|
|
||||||
/* loop through the list of requests, and ack all related */
|
|
||||||
for (Request *r = _r_list.first(), *r_to_handle = r; r;
|
|
||||||
r_to_handle = r) {
|
|
||||||
r = r->next();
|
|
||||||
if (r_to_handle->match(p)) {
|
|
||||||
_handle_reply(p, r_to_handle);
|
|
||||||
_r_list.remove(r_to_handle);
|
|
||||||
Genode::destroy(&_r_slab, r_to_handle);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_blk.tx()->release_packet(p);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Handle that the backend device is ready to receive again
|
|
||||||
*/
|
|
||||||
void _ready_to_submit() { }
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Setup a request to the backend device
|
|
||||||
*
|
|
||||||
* \param block_number block number offset
|
|
||||||
* \param block_count number of blocks
|
|
||||||
* \param packet original packet request received from the client
|
|
||||||
*/
|
|
||||||
void _request(Block::sector_t block_number,
|
|
||||||
Genode::size_t block_count,
|
|
||||||
char * const buffer,
|
|
||||||
Block::Packet_descriptor &packet)
|
|
||||||
{
|
|
||||||
Block::Packet_descriptor p_to_dev;
|
|
||||||
|
|
||||||
try {
|
|
||||||
/* we've to look whether the request is already pending */
|
|
||||||
for (Request *r = _r_list.first(); r; r = r->next()) {
|
|
||||||
if (r->match(false, block_number, block_count)) {
|
|
||||||
_r_list.insert(new (&_r_slab) Request(r->srv, packet,
|
|
||||||
buffer));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* it doesn't pay, we've to send a request to the device */
|
|
||||||
if (!_blk.tx()->ready_to_submit()) {
|
|
||||||
Genode::warning("not ready_to_submit");
|
|
||||||
throw Request_congestion();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* read ahead CACHE_BLK_SIZE */
|
|
||||||
Block::sector_t nr = _cache_blk_round_off(block_number);
|
|
||||||
Genode::size_t cnt = _cache_blk_round_up(block_count +
|
|
||||||
(block_number - nr));
|
|
||||||
|
|
||||||
/* ensure all memory is available before sending the request */
|
|
||||||
_cache.alloc(cnt * _info.block_size, nr * _info.block_size);
|
|
||||||
|
|
||||||
/* construct and send the packet */
|
|
||||||
p_to_dev =
|
|
||||||
Block::Packet_descriptor(_blk.alloc_packet(_info.block_size*cnt),
|
|
||||||
Block::Packet_descriptor::READ,
|
|
||||||
nr, cnt);
|
|
||||||
_r_list.insert(new (&_r_slab) Request(p_to_dev, packet, buffer));
|
|
||||||
_blk.tx()->submit_packet(p_to_dev);
|
|
||||||
} catch(Block::Session::Tx::Source::Packet_alloc_failed) {
|
|
||||||
throw Request_congestion();
|
|
||||||
} catch(Genode::Allocator::Out_of_memory) {
|
|
||||||
/* clean up */
|
|
||||||
_blk.tx()->release_packet(p_to_dev);
|
|
||||||
throw Request_congestion();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Synchronize dirty chunks with backend device
|
|
||||||
*/
|
|
||||||
void _sync()
|
|
||||||
{
|
|
||||||
Cache::offset_t off = 0;
|
|
||||||
Cache::size_t len = _info.block_size * _info.block_count;
|
|
||||||
|
|
||||||
while (len > 0) {
|
|
||||||
try {
|
|
||||||
_cache.sync(len, off);
|
|
||||||
len = 0;
|
|
||||||
} catch(Write_failed &e) {
|
|
||||||
/**
|
|
||||||
* Write to backend failed when backend device isn't ready
|
|
||||||
* to proceed, so handle signals, until it's ready again
|
|
||||||
*/
|
|
||||||
off = e.off;
|
|
||||||
len = _info.block_size * _info.block_count - off;
|
|
||||||
_env.ep().wait_and_dispatch_one_io_signal();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Check for chunk availability
|
|
||||||
*
|
|
||||||
* \param nr block number offset
|
|
||||||
* \param cnt number of blocks
|
|
||||||
* \param p client side packet, which triggered this operation
|
|
||||||
*/
|
|
||||||
bool _stat(Block::sector_t nr, Genode::size_t cnt,
|
|
||||||
char * const buffer, Block::Packet_descriptor &p)
|
|
||||||
{
|
|
||||||
Cache::offset_t off = nr * _info.block_size;
|
|
||||||
Cache::size_t size = cnt * _info.block_size;
|
|
||||||
Cache::offset_t end = off + size;
|
|
||||||
|
|
||||||
try {
|
|
||||||
_cache.stat(size, off);
|
|
||||||
return true;
|
|
||||||
} catch(Cache::Chunk_base::Range_incomplete &e) {
|
|
||||||
off = Genode::max(off, e.off);
|
|
||||||
size = Genode::min(end - off, e.size);
|
|
||||||
_request(off / _info.block_size, size / _info.block_size, buffer, p);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Signal handler for yield requests of the parent
|
|
||||||
*/
|
|
||||||
void _parent_yield()
|
|
||||||
{
|
|
||||||
using namespace Genode;
|
|
||||||
|
|
||||||
Parent::Resource_args const args = _env.parent().yield_request();
|
|
||||||
size_t const requested_ram_quota =
|
|
||||||
Arg_string::find_arg(args.string(), "ram_quota").ulong_value(0);
|
|
||||||
|
|
||||||
/* flush the requested amount of RAM from cache */
|
|
||||||
POLICY::flush(requested_ram_quota);
|
|
||||||
_env.parent().yield_response();
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Constructor
|
|
||||||
*
|
|
||||||
* \param ep server entrypoint
|
|
||||||
*/
|
|
||||||
Driver(Genode::Env &env, Genode::Heap &heap)
|
|
||||||
: Block::Driver(env.ram()),
|
|
||||||
_env(env),
|
|
||||||
_r_slab(&heap),
|
|
||||||
_alloc(&heap, CACHE_BLK_SIZE),
|
|
||||||
_blk(_env, &_alloc, Block::Session::TX_QUEUE_SIZE*CACHE_BLK_SIZE),
|
|
||||||
_info(_blk.info()),
|
|
||||||
_cache(heap, 0),
|
|
||||||
_source_ack(env.ep(), *this, &Driver::_ack_avail),
|
|
||||||
_source_submit(env.ep(), *this, &Driver::_ready_to_submit),
|
|
||||||
_yield(env.ep(), *this, &Driver::_parent_yield)
|
|
||||||
{
|
|
||||||
using namespace Genode;
|
|
||||||
|
|
||||||
_blk.tx_channel()->sigh_ack_avail(_source_ack);
|
|
||||||
_blk.tx_channel()->sigh_ready_to_submit(_source_submit);
|
|
||||||
env.parent().yield_sigh(_yield);
|
|
||||||
|
|
||||||
if (CACHE_BLK_SIZE % _info.block_size) {
|
|
||||||
error("only devices that block size is divider of ",
|
|
||||||
Hex(CACHE_BLK_SIZE, Hex::OMIT_PREFIX) ," supported");
|
|
||||||
throw Io_error();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* truncate chunk structure to real size of the device */
|
|
||||||
_cache.truncate(_info.block_size * _info.block_count);
|
|
||||||
}
|
|
||||||
|
|
||||||
~Driver()
|
|
||||||
{
|
|
||||||
/* when session gets closed, synchronize and flush the cache */
|
|
||||||
_sync();
|
|
||||||
POLICY::flush();
|
|
||||||
}
|
|
||||||
|
|
||||||
Block::Session_client* blk() { return &_blk; }
|
|
||||||
Genode::size_t blk_sz() { return _info.block_size; }
|
|
||||||
|
|
||||||
|
|
||||||
/****************************
|
|
||||||
** Block-driver interface **
|
|
||||||
****************************/
|
|
||||||
|
|
||||||
Block::Session::Info info() const override { return _info; }
|
|
||||||
|
|
||||||
void read(Block::sector_t block_number,
|
|
||||||
Genode::size_t block_count,
|
|
||||||
char* buffer,
|
|
||||||
Block::Packet_descriptor &packet)
|
|
||||||
{
|
|
||||||
if (!_stat(block_number, block_count, buffer, packet))
|
|
||||||
return;
|
|
||||||
|
|
||||||
_cache.read(buffer,
|
|
||||||
block_count *_info.block_size,
|
|
||||||
block_number*_info.block_size);
|
|
||||||
|
|
||||||
ack_packet(packet);
|
|
||||||
}
|
|
||||||
|
|
||||||
void write(Block::sector_t block_number,
|
|
||||||
Genode::size_t block_count,
|
|
||||||
const char * buffer,
|
|
||||||
Block::Packet_descriptor &packet)
|
|
||||||
{
|
|
||||||
if (!_info.writeable)
|
|
||||||
throw Io_error();
|
|
||||||
|
|
||||||
_cache.alloc(block_count * _info.block_size,
|
|
||||||
block_number * _info.block_size);
|
|
||||||
|
|
||||||
if ((block_number % _cache_blk_mod()) &&
|
|
||||||
!_stat(block_number, 1, const_cast<char* const>(buffer), packet))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (((block_number+block_count) % _cache_blk_mod())
|
|
||||||
&& !_stat(block_number+block_count-1, 1,
|
|
||||||
const_cast<char* const>(buffer), packet))
|
|
||||||
return;
|
|
||||||
|
|
||||||
_cache.write(buffer,
|
|
||||||
block_count * _info.block_size,
|
|
||||||
block_number * _info.block_size);
|
|
||||||
|
|
||||||
ack_packet(packet);
|
|
||||||
}
|
|
||||||
|
|
||||||
void sync() { _sync(); }
|
|
||||||
};
|
|
@ -1,62 +0,0 @@
|
|||||||
/*
|
|
||||||
* \brief Least-recently-used cache replacement strategy
|
|
||||||
* \author Stefan Kalkowski
|
|
||||||
* \date 2013-12-05
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Copyright (C) 2013-2017 Genode Labs GmbH
|
|
||||||
*
|
|
||||||
* This file is part of the Genode OS framework, which is distributed
|
|
||||||
* under the terms of the GNU Affero General Public License version 3.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "lru.h"
|
|
||||||
#include "driver.h"
|
|
||||||
|
|
||||||
typedef Driver<Lru_policy>::Chunk_level_4 Chunk;
|
|
||||||
|
|
||||||
static const Lru_policy::Element *lru = 0;
|
|
||||||
static Genode::List<Lru_policy::Element> lru_list;
|
|
||||||
|
|
||||||
|
|
||||||
static void lru_access(const Lru_policy::Element *e)
|
|
||||||
{
|
|
||||||
if (e == lru) return;
|
|
||||||
|
|
||||||
if (e->next()) lru_list.remove(e);
|
|
||||||
|
|
||||||
lru_list.insert(e, lru);
|
|
||||||
lru = e;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void Lru_policy::read(const Lru_policy::Element *e) {
|
|
||||||
lru_access(e); }
|
|
||||||
|
|
||||||
|
|
||||||
void Lru_policy::write(const Lru_policy::Element *e) {
|
|
||||||
lru_access(e); }
|
|
||||||
|
|
||||||
|
|
||||||
void Lru_policy::flush(Cache::size_t size)
|
|
||||||
{
|
|
||||||
Cache::size_t s = 0;
|
|
||||||
for (Lru_policy::Element *e = lru_list.first();
|
|
||||||
e && ((size == 0) || (s < size));
|
|
||||||
e = lru_list.first(), s += sizeof(Chunk)) {
|
|
||||||
Chunk *cb = static_cast<Chunk*>(e);
|
|
||||||
e = e->next();
|
|
||||||
try {
|
|
||||||
cb->free(Driver<Lru_policy>::CACHE_BLK_SIZE,
|
|
||||||
cb->base_offset());
|
|
||||||
lru_list.remove(cb);
|
|
||||||
} catch(Chunk::Dirty_chunk &e) {
|
|
||||||
cb->sync(e.size, e.off);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!lru_list.first()) lru = 0;
|
|
||||||
|
|
||||||
if (s < size) throw Block::Driver::Request_congestion();
|
|
||||||
}
|
|
@ -1,25 +0,0 @@
|
|||||||
/*
|
|
||||||
* \brief Least-recently-used cache replacement strategy
|
|
||||||
* \author Stefan Kalkowski
|
|
||||||
* \date 2013-12-05
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Copyright (C) 2013-2017 Genode Labs GmbH
|
|
||||||
*
|
|
||||||
* This file is part of the Genode OS framework, which is distributed
|
|
||||||
* under the terms of the GNU Affero General Public License version 3.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <util/list.h>
|
|
||||||
|
|
||||||
#include "chunk.h"
|
|
||||||
|
|
||||||
struct Lru_policy
|
|
||||||
{
|
|
||||||
class Element : public Genode::List<Element>::Element {};
|
|
||||||
|
|
||||||
static void read(const Element *e);
|
|
||||||
static void write(const Element *e);
|
|
||||||
static void flush(Cache::size_t size = 0);
|
|
||||||
};
|
|
@ -1,94 +0,0 @@
|
|||||||
/*
|
|
||||||
* \brief Cache a block device
|
|
||||||
* \author Stefan Kalkowski
|
|
||||||
* \date 2013-12-05
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Copyright (C) 2013-2017 Genode Labs GmbH
|
|
||||||
*
|
|
||||||
* This file is part of the Genode OS framework, which is distributed
|
|
||||||
* under the terms of the GNU Affero General Public License version 3.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <base/component.h>
|
|
||||||
|
|
||||||
#include "lru.h"
|
|
||||||
#include "driver.h"
|
|
||||||
|
|
||||||
using Policy = Lru_policy;
|
|
||||||
static Driver<Policy> * driver = nullptr;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Synchronize a chunk with the backend device
|
|
||||||
*/
|
|
||||||
template <typename POLICY>
|
|
||||||
void Driver<POLICY>::Policy::sync(const typename POLICY::Element *e, char *dst)
|
|
||||||
{
|
|
||||||
Cache::offset_t off =
|
|
||||||
static_cast<const Driver<POLICY>::Chunk_level_4*>(e)->base_offset();
|
|
||||||
|
|
||||||
if (!driver) throw Write_failed(off);
|
|
||||||
|
|
||||||
if (!driver->blk()->tx()->ready_to_submit())
|
|
||||||
throw Write_failed(off);
|
|
||||||
try {
|
|
||||||
Block::Packet_descriptor
|
|
||||||
p(driver->blk()->alloc_packet(Driver::CACHE_BLK_SIZE),
|
|
||||||
Block::Packet_descriptor::WRITE, off / driver->blk_sz(),
|
|
||||||
Driver::CACHE_BLK_SIZE / driver->blk_sz());
|
|
||||||
driver->blk()->tx()->submit_packet(p);
|
|
||||||
} catch(Block::Session::Tx::Source::Packet_alloc_failed) {
|
|
||||||
throw Write_failed(off);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* explicit instantiation for external reference */
|
|
||||||
template void Driver<Policy>::Policy::sync(const typename Policy::Element *, char *);
|
|
||||||
|
|
||||||
|
|
||||||
struct Main
|
|
||||||
{
|
|
||||||
template <typename T>
|
|
||||||
struct Factory : Block::Driver_factory
|
|
||||||
{
|
|
||||||
Genode::Env &env;
|
|
||||||
Genode::Heap &heap;
|
|
||||||
|
|
||||||
Factory(Genode::Env &env, Genode::Heap &heap) : env(env), heap(heap) {}
|
|
||||||
|
|
||||||
Block::Driver *create()
|
|
||||||
{
|
|
||||||
driver = new (&heap) ::Driver<T>(env, heap);
|
|
||||||
return driver;
|
|
||||||
}
|
|
||||||
|
|
||||||
void destroy(Block::Driver *driver) {
|
|
||||||
Genode::destroy(&heap, static_cast<::Driver<T>*>(driver)); }
|
|
||||||
};
|
|
||||||
|
|
||||||
void resource_handler() { }
|
|
||||||
|
|
||||||
Genode::Env &env;
|
|
||||||
Genode::Heap heap { env.ram(), env.rm() };
|
|
||||||
Factory<Lru_policy> factory { env, heap };
|
|
||||||
Block::Root root { env.ep(), heap, env.rm(), factory, true };
|
|
||||||
Genode::Signal_handler<Main> resource_dispatcher {
|
|
||||||
env.ep(), *this, &Main::resource_handler };
|
|
||||||
|
|
||||||
Main(Genode::Env &env) : env(env)
|
|
||||||
{
|
|
||||||
env.parent().announce(env.ep().manage(root));
|
|
||||||
env.parent().resource_avail_sigh(resource_dispatcher);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
void Component::construct(Genode::Env &env)
|
|
||||||
{
|
|
||||||
/* XXX execute constructors of global statics */
|
|
||||||
env.exec_static_constructors();
|
|
||||||
|
|
||||||
static Main server(env);
|
|
||||||
}
|
|
@ -1,5 +0,0 @@
|
|||||||
TARGET = block_cache
|
|
||||||
LIBS = base
|
|
||||||
SRC_CC = main.cc lru.cc
|
|
||||||
|
|
||||||
CC_CXX_WARN_STRICT =
|
|
Reference in New Issue
Block a user