genode/repos/base/include/base/allocator_avl.h

421 lines
12 KiB
C
Raw Normal View History

2011-12-22 16:19:25 +01:00
/*
* \brief Interface of AVL-tree-based allocator
* \author Norman Feske
* \date 2006-04-16
*/
/*
* Copyright (C) 2006-2017 Genode Labs GmbH
2011-12-22 16:19:25 +01:00
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
2011-12-22 16:19:25 +01:00
*/
#ifndef _INCLUDE__BASE__ALLOCATOR_AVL_H_
#define _INCLUDE__BASE__ALLOCATOR_AVL_H_
#include <base/allocator.h>
#include <base/tslab.h>
#include <base/output.h>
2011-12-22 16:19:25 +01:00
#include <util/avl_tree.h>
#include <util/misc_math.h>
#include <util/construct_at.h>
2011-12-22 16:19:25 +01:00
namespace Genode {
class Allocator_avl_base;
2011-12-22 16:19:25 +01:00
/*
* The default slab block size is dimensioned such that slab-block
* allocations make effective use of entire memory pages. To account for
* the common pattern of using a 'Sliced_heap' as backing store for the
* 'Allocator_avl'. We remove 8 words from the slab-block size to take the
* meta-data overhead of each sliced-heap block into account.
*/
template <typename, unsigned SLAB_BLOCK_SIZE = (1024 - 8)*sizeof(addr_t)>
class Allocator_avl_tpl;
2011-12-22 16:19:25 +01:00
/**
* Define AVL-based allocator without any meta data attached to each block
*/
class Empty { };
typedef Allocator_avl_tpl<Empty> Allocator_avl;
}
2011-12-22 16:19:25 +01:00
class Genode::Allocator_avl_base : public Range_allocator
{
private:
2011-12-22 16:19:25 +01:00
static bool _sum_in_range(addr_t addr, addr_t offset) {
return (~0UL - addr > offset); }
2011-12-22 16:19:25 +01:00
Follow practices suggested by "Effective C++" The patch adjust the code of the base, base-<kernel>, and os repository. To adapt existing components to fix violations of the best practices suggested by "Effective C++" as reported by the -Weffc++ compiler argument. The changes follow the patterns outlined below: * A class with virtual functions can no longer publicly inherit base classed without a vtable. The inherited object may either be moved to a member variable, or inherited privately. The latter would be used for classes that inherit 'List::Element' or 'Avl_node'. In order to enable the 'List' and 'Avl_tree' to access the meta data, the 'List' must become a friend. * Instead of adding a virtual destructor to abstract base classes, we inherit the new 'Interface' class, which contains a virtual destructor. This way, single-line abstract base classes can stay as compact as they are now. The 'Interface' utility resides in base/include/util/interface.h. * With the new warnings enabled, all member variables must be explicitly initialized. Basic types may be initialized with '='. All other types are initialized with braces '{ ... }' or as class initializers. If basic types and non-basic types appear in a row, it is nice to only use the brace syntax (also for basic types) and align the braces. * If a class contains pointers as members, it must now also provide a copy constructor and assignment operator. In the most cases, one would make them private, effectively disallowing the objects to be copied. Unfortunately, this warning cannot be fixed be inheriting our existing 'Noncopyable' class (the compiler fails to detect that the inheriting class cannot be copied and still gives the error). For now, we have to manually add declarations for both the copy constructor and assignment operator as private class members. Those declarations should be prepended with a comment like this: /* * Noncopyable */ Thread(Thread const &); Thread &operator = (Thread const &); In the future, we should revisit these places and try to replace the pointers with references. In the presence of at least one reference member, the compiler would no longer implicitly generate a copy constructor. So we could remove the manual declaration. Issue #465
2017-12-21 15:42:15 +01:00
/*
* Noncopyable
*/
Allocator_avl_base(Allocator_avl_base const &);
Allocator_avl_base &operator = (Allocator_avl_base const &);
protected:
2011-12-22 16:19:25 +01:00
class Block : public Avl_node<Block>
{
private:
2011-12-22 16:19:25 +01:00
Follow practices suggested by "Effective C++" The patch adjust the code of the base, base-<kernel>, and os repository. To adapt existing components to fix violations of the best practices suggested by "Effective C++" as reported by the -Weffc++ compiler argument. The changes follow the patterns outlined below: * A class with virtual functions can no longer publicly inherit base classed without a vtable. The inherited object may either be moved to a member variable, or inherited privately. The latter would be used for classes that inherit 'List::Element' or 'Avl_node'. In order to enable the 'List' and 'Avl_tree' to access the meta data, the 'List' must become a friend. * Instead of adding a virtual destructor to abstract base classes, we inherit the new 'Interface' class, which contains a virtual destructor. This way, single-line abstract base classes can stay as compact as they are now. The 'Interface' utility resides in base/include/util/interface.h. * With the new warnings enabled, all member variables must be explicitly initialized. Basic types may be initialized with '='. All other types are initialized with braces '{ ... }' or as class initializers. If basic types and non-basic types appear in a row, it is nice to only use the brace syntax (also for basic types) and align the braces. * If a class contains pointers as members, it must now also provide a copy constructor and assignment operator. In the most cases, one would make them private, effectively disallowing the objects to be copied. Unfortunately, this warning cannot be fixed be inheriting our existing 'Noncopyable' class (the compiler fails to detect that the inheriting class cannot be copied and still gives the error). For now, we have to manually add declarations for both the copy constructor and assignment operator as private class members. Those declarations should be prepended with a comment like this: /* * Noncopyable */ Thread(Thread const &); Thread &operator = (Thread const &); In the future, we should revisit these places and try to replace the pointers with references. In the presence of at least one reference member, the compiler would no longer implicitly generate a copy constructor. So we could remove the manual declaration. Issue #465
2017-12-21 15:42:15 +01:00
addr_t _addr { 0 }; /* base address */
size_t _size { 0 }; /* size of block */
bool _used { false }; /* block is in use */
short _id { 0 }; /* for debugging */
size_t _max_avail { 0 }; /* biggest free block size of
sub tree */
2011-12-22 16:19:25 +01:00
/**
* Request max_avail value of subtree
*/
inline size_t _child_max_avail(bool side) {
return child(side) ? child(side)->max_avail() : 0; }
/**
* Query if block can hold a specified subblock
*
* \param n number of bytes
* \param from minimum start address of subblock
* \param to maximum end address of subblock
* \param align alignment (power of two)
* \return true if block fits
*/
inline bool _fits(size_t n, unsigned align,
addr_t from, addr_t to)
{
addr_t a = align_addr(addr() < from ? from : addr(),
align);
return (a >= addr()) && _sum_in_range(a, n) &&
(a - addr() + n <= avail()) && (a + n - 1 <= to);
}
Follow practices suggested by "Effective C++" The patch adjust the code of the base, base-<kernel>, and os repository. To adapt existing components to fix violations of the best practices suggested by "Effective C++" as reported by the -Weffc++ compiler argument. The changes follow the patterns outlined below: * A class with virtual functions can no longer publicly inherit base classed without a vtable. The inherited object may either be moved to a member variable, or inherited privately. The latter would be used for classes that inherit 'List::Element' or 'Avl_node'. In order to enable the 'List' and 'Avl_tree' to access the meta data, the 'List' must become a friend. * Instead of adding a virtual destructor to abstract base classes, we inherit the new 'Interface' class, which contains a virtual destructor. This way, single-line abstract base classes can stay as compact as they are now. The 'Interface' utility resides in base/include/util/interface.h. * With the new warnings enabled, all member variables must be explicitly initialized. Basic types may be initialized with '='. All other types are initialized with braces '{ ... }' or as class initializers. If basic types and non-basic types appear in a row, it is nice to only use the brace syntax (also for basic types) and align the braces. * If a class contains pointers as members, it must now also provide a copy constructor and assignment operator. In the most cases, one would make them private, effectively disallowing the objects to be copied. Unfortunately, this warning cannot be fixed be inheriting our existing 'Noncopyable' class (the compiler fails to detect that the inheriting class cannot be copied and still gives the error). For now, we have to manually add declarations for both the copy constructor and assignment operator as private class members. Those declarations should be prepended with a comment like this: /* * Noncopyable */ Thread(Thread const &); Thread &operator = (Thread const &); In the future, we should revisit these places and try to replace the pointers with references. In the presence of at least one reference member, the compiler would no longer implicitly generate a copy constructor. So we could remove the manual declaration. Issue #465
2017-12-21 15:42:15 +01:00
/*
* Noncopyable
*/
Block(Block const &);
Block &operator = (Block const &);
public:
/**
* Avl_node interface: compare two nodes
*/
bool higher(Block *a) {
return a->_addr >= _addr; }
2011-12-22 16:19:25 +01:00
/**
* Avl_node interface: update meta data on node rearrangement
*/
void recompute();
2011-12-22 16:19:25 +01:00
2015-03-24 12:04:37 +01:00
/*****************
** Accessorors **
*****************/
inline int id() const { return _id; }
inline addr_t addr() const { return _addr; }
inline size_t avail() const { return _used ? 0 : _size; }
inline size_t size() const { return _size; }
inline bool used() const { return _used; }
inline size_t max_avail() const { return _max_avail; }
inline void used(bool used) { _used = used; }
enum { FREE = false, USED = true };
/**
* Constructor
*
* This constructor is called from meta-data allocator during
* initialization of new meta-data blocks.
*/
Follow practices suggested by "Effective C++" The patch adjust the code of the base, base-<kernel>, and os repository. To adapt existing components to fix violations of the best practices suggested by "Effective C++" as reported by the -Weffc++ compiler argument. The changes follow the patterns outlined below: * A class with virtual functions can no longer publicly inherit base classed without a vtable. The inherited object may either be moved to a member variable, or inherited privately. The latter would be used for classes that inherit 'List::Element' or 'Avl_node'. In order to enable the 'List' and 'Avl_tree' to access the meta data, the 'List' must become a friend. * Instead of adding a virtual destructor to abstract base classes, we inherit the new 'Interface' class, which contains a virtual destructor. This way, single-line abstract base classes can stay as compact as they are now. The 'Interface' utility resides in base/include/util/interface.h. * With the new warnings enabled, all member variables must be explicitly initialized. Basic types may be initialized with '='. All other types are initialized with braces '{ ... }' or as class initializers. If basic types and non-basic types appear in a row, it is nice to only use the brace syntax (also for basic types) and align the braces. * If a class contains pointers as members, it must now also provide a copy constructor and assignment operator. In the most cases, one would make them private, effectively disallowing the objects to be copied. Unfortunately, this warning cannot be fixed be inheriting our existing 'Noncopyable' class (the compiler fails to detect that the inheriting class cannot be copied and still gives the error). For now, we have to manually add declarations for both the copy constructor and assignment operator as private class members. Those declarations should be prepended with a comment like this: /* * Noncopyable */ Thread(Thread const &); Thread &operator = (Thread const &); In the future, we should revisit these places and try to replace the pointers with references. In the presence of at least one reference member, the compiler would no longer implicitly generate a copy constructor. So we could remove the manual declaration. Issue #465
2017-12-21 15:42:15 +01:00
Block();
/**
* Constructor
*/
Block(addr_t addr, size_t size, bool used)
: _addr(addr), _size(size), _used(used),
_max_avail(used ? 0 : size)
{
static int num_blocks;
_id = ++num_blocks;
}
/**
* Find best-fitting block
*/
Block *find_best_fit(size_t size, unsigned align,
addr_t from = 0UL, addr_t to = ~0UL);
2011-12-22 16:19:25 +01:00
/**
* Find block that contains the specified address range
*/
Block *find_by_address(addr_t addr, size_t size = 0,
bool check_overlap = 0);
2011-12-22 16:19:25 +01:00
/**
* Return sum of available memory in subtree
*/
size_t avail_in_subtree(void);
};
private:
Follow practices suggested by "Effective C++" The patch adjust the code of the base, base-<kernel>, and os repository. To adapt existing components to fix violations of the best practices suggested by "Effective C++" as reported by the -Weffc++ compiler argument. The changes follow the patterns outlined below: * A class with virtual functions can no longer publicly inherit base classed without a vtable. The inherited object may either be moved to a member variable, or inherited privately. The latter would be used for classes that inherit 'List::Element' or 'Avl_node'. In order to enable the 'List' and 'Avl_tree' to access the meta data, the 'List' must become a friend. * Instead of adding a virtual destructor to abstract base classes, we inherit the new 'Interface' class, which contains a virtual destructor. This way, single-line abstract base classes can stay as compact as they are now. The 'Interface' utility resides in base/include/util/interface.h. * With the new warnings enabled, all member variables must be explicitly initialized. Basic types may be initialized with '='. All other types are initialized with braces '{ ... }' or as class initializers. If basic types and non-basic types appear in a row, it is nice to only use the brace syntax (also for basic types) and align the braces. * If a class contains pointers as members, it must now also provide a copy constructor and assignment operator. In the most cases, one would make them private, effectively disallowing the objects to be copied. Unfortunately, this warning cannot be fixed be inheriting our existing 'Noncopyable' class (the compiler fails to detect that the inheriting class cannot be copied and still gives the error). For now, we have to manually add declarations for both the copy constructor and assignment operator as private class members. Those declarations should be prepended with a comment like this: /* * Noncopyable */ Thread(Thread const &); Thread &operator = (Thread const &); In the future, we should revisit these places and try to replace the pointers with references. In the presence of at least one reference member, the compiler would no longer implicitly generate a copy constructor. So we could remove the manual declaration. Issue #465
2017-12-21 15:42:15 +01:00
Avl_tree<Block> _addr_tree { }; /* blocks sorted by base address */
Allocator *_md_alloc { nullptr }; /* meta-data allocator */
size_t _md_entry_size { 0 }; /* size of block meta-data entry */
/**
* Alloc meta-data block
*/
Block *_alloc_block_metadata();
/**
* Alloc two meta-data blocks in a transactional way
*/
bool _alloc_two_blocks_metadata(Block **dst1, Block **dst2);
/**
* Create new block
*/
int _add_block(Block *block_metadata,
addr_t base, size_t size, bool used);
Block *_find_any_used_block(Block *sub_tree);
/**
* Destroy block
*/
void _destroy_block(Block *b);
/**
* Cut specified area from block
*
* The original block gets replaced by (up to) two smaller blocks
* with remaining space.
*/
void _cut_from_block(Block *b, addr_t cut_addr, size_t cut_size,
Block *dst1, Block *dst2);
protected:
Avl_tree<Block> const & _block_tree() const { return _addr_tree; }
/**
* Clean up the allocator and detect dangling allocations
*
* This function is called at the destruction time of the allocator. It
* makes sure that the allocator instance releases all memory obtained
* from the meta-data allocator.
*/
void _revert_allocations_and_ranges();
/**
* Find block by specified address
*/
Block *_find_by_address(addr_t addr, size_t size = 0,
bool check_overlap = 0) const
{
Block *b = static_cast<Block *>(_addr_tree.first());
/* if the tree has one or more nodes, start search */
return b ? b->find_by_address(addr, size, check_overlap) : 0;
}
/**
* Constructor
*
* This constructor can only be called from a derived class that
* provides an allocator for block meta-data entries. This way,
* we can attach custom information to block meta data.
*/
Allocator_avl_base(Allocator *md_alloc, size_t md_entry_size) :
_md_alloc(md_alloc), _md_entry_size(md_entry_size) { }
~Allocator_avl_base() { _revert_allocations_and_ranges(); }
public:
/**
* Return address of any block of the allocator
*
* \param out_addr result that contains address of block
* \return true if block was found or
* false if there is no block available
*
* If no block was found, out_addr is set to zero.
*/
bool any_block_addr(addr_t *out_addr);
void print(Output &out) const;
/*******************************
** Range allocator interface **
*******************************/
2015-03-23 19:58:27 +01:00
int add_range(addr_t base, size_t size) override;
int remove_range(addr_t base, size_t size) override;
Alloc_return alloc_aligned(size_t size, void **out_addr, int align,
2015-03-23 19:58:27 +01:00
addr_t from = 0, addr_t to = ~0UL) override;
Alloc_return alloc_addr(size_t size, addr_t addr) override;
void free(void *addr) override;
size_t avail() const override;
bool valid_addr(addr_t addr) const override;
/*************************
** Allocator interface **
*************************/
bool alloc(size_t size, void **out_addr) override
{
return (Allocator_avl_base::alloc_aligned(
size, out_addr, log2(sizeof(addr_t))).ok());
}
void free(void *addr, size_t) override { free(addr); }
/**
* Return size of block at specified address
*/
size_t size_at(void const *addr) const;
/**
* Return the memory overhead per Block
*
* The overhead is a rough estimation. If a block is somewhere
* in the middle of a free area, we could consider the meta data
* for the two free subareas when calculating the overhead.
*
* The 'sizeof(umword_t)' represents the overhead of the meta-data
* slab allocator.
*/
size_t overhead(size_t) const override { return sizeof(Block) + sizeof(umword_t); }
bool need_size_for_free() const override { return false; }
};
/**
* AVL-based allocator with custom meta data attached to each block.
*
* \param BMDT block meta-data type
*/
template <typename BMDT, unsigned SLAB_BLOCK_SIZE>
class Genode::Allocator_avl_tpl : public Allocator_avl_base
{
protected:
/*
* Pump up the Block class with custom meta-data type
*/
class Block : public Allocator_avl_base::Block, public BMDT { };
Tslab<Block,SLAB_BLOCK_SIZE> _metadata; /* meta-data allocator */
char _initial_md_block[SLAB_BLOCK_SIZE]; /* first (static) meta-data block */
public:
struct Assign_metadata_failed : Exception { };
/**
* Constructor
*
* \param metadata_chunk_alloc pointer to allocator used to allocate
* meta-data blocks. If set to 0,
* use ourself for allocating our
* meta-data blocks. This works only
* if the managed memory is completely
* accessible by the allocator.
*/
explicit Allocator_avl_tpl(Allocator *metadata_chunk_alloc) :
Allocator_avl_base(&_metadata, sizeof(Block)),
_metadata((metadata_chunk_alloc) ? metadata_chunk_alloc : this,
(Slab_block *)&_initial_md_block) { }
~Allocator_avl_tpl() { _revert_allocations_and_ranges(); }
/**
* Return size of slab blocks used for meta data
*/
static constexpr size_t slab_block_size() { return SLAB_BLOCK_SIZE; }
/**
* Assign custom meta data to block at specified address
*
* \throw Assign_metadata_failed
*/
void metadata(void *addr, BMDT bmd) const
{
Block * const b = static_cast<Block *>(_find_by_address((addr_t)addr));
if (b) *static_cast<BMDT *>(b) = bmd;
else throw Assign_metadata_failed();
}
/**
* Construct meta-data object in place
*
* \param ARGS arguments passed to the meta-data constuctor
*/
template <typename... ARGS>
void construct_metadata(void *addr, ARGS &&... args)
{
Block * const b = static_cast<Block *>(_find_by_address((addr_t)addr));
if (b) construct_at<BMDT>(static_cast<BMDT *>(b), args...);
else throw Assign_metadata_failed();
}
/**
* Return meta data that was attached to block at specified address
*/
BMDT* metadata(void *addr) const
{
Block *b = static_cast<Block *>(_find_by_address((addr_t)addr));
return b && b->used() ? b : 0;
}
int add_range(addr_t base, size_t size) override
{
2011-12-22 16:19:25 +01:00
/*
* We disable the slab block allocation while
* processing add_range to prevent avalanche
* effects when (slab trying to make an allocation
* at Allocator_avl that is empty).
2011-12-22 16:19:25 +01:00
*/
Allocator *md_bs = _metadata.backing_store();
_metadata.backing_store(0);
int ret = Allocator_avl_base::add_range(base, size);
_metadata.backing_store(md_bs);
return ret;
}
/**
* Apply functor 'fn' to the metadata of an arbitrary
* member of the allocator. This method is provided for
* destructing each member of the allocator. Calling
* the method repeatedly without removing or inserting
* members will produce the same member.
*/
template <typename FUNC>
bool apply_any(FUNC const &fn)
{
addr_t addr = 0;
if (any_block_addr(&addr)) {
if (BMDT *b = metadata((void*)addr)) {
fn((BMDT&)*b);
return true;
}
}
return false;
}
};
2011-12-22 16:19:25 +01:00
#endif /* _INCLUDE__BASE__ALLOCATOR_AVL_H_ */