genode/dde_linux/src/drivers/usb/mem.h
Sebastian Sumpf 3207b4eed0 USB: Performance improvements
Added SKB bitmap allocator, use Nic::Packet_allocator in packet stream, use slab
allocators on top of back-end AVL allocator, split allocators in cached/uncached
for general purpose/DMA allocation, added patch to original code to distinguish
cached or uncached memory requests, take advantage of and implement TX bursts
(or SKB batching), call interrupt handlers until they return unhandled.
2012-08-07 22:22:45 +02:00

238 lines
5.2 KiB
C++

/*
* \brief Memory pool
* \author Sebastian Sumpf
* \date 2012-06-18
*/
/*
* Copyright (C) 2012 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _MEM_H_
#define _MEM_H_
#include <base/allocator_avl.h>
#include <dataspace/client.h>
#include <lx_emul.h>
/*********************
** linux/dmapool.h **
*********************/
namespace Genode {
/**
* Memory back-end
*/
class Mem
{
/* configurable sizes of memory pools */
enum
{
MEM_POOL = 2 * 1024 * 1024,
DMA_POOL = 3 * 1024 * 1024,
};
private:
addr_t _base; /* virt base of pool */
addr_t _base_phys; /* phys base of pool */
size_t _size; /* size of backng store */
Allocator_avl _range; /* range allocator for pool */
addr_t *_zones; /* bases of zones */
int _zone_count; /* number of zones */
int _zone_alloc; /* currently allocated zones */
Ram_dataspace_capability _ds_cap; /* backing store */
/**
* Private constructor
*/
Mem(size_t size, bool cached = true)
: _size(size), _range(env()->heap()),_zone_count(0), _zone_alloc(0)
{
_ds_cap = env()->ram_session()->alloc(_size, cached);
_base_phys = Dataspace_client(_ds_cap).phys_addr();
_base = (addr_t)env()->rm_session()->attach(_ds_cap);
dde_kit_log(DEBUG_DMA, "New DMA range [%lx-%lx)", _base, _base + _size);
_range.add_range(_base, _size);
}
/**
* Convert 'Mem' addres to zone address
*/
void *_to_zone(void const *addr, int i)
{
if (i < 0)
return (void *)addr;
addr_t zone_base = _zones[i];
addr_t offset = (addr_t)addr - _base;
return (void *)(zone_base + offset);
}
/**
* Convert zone addres to 'Mem' address
*/
void *_from_zone(void const *addr, int i)
{
if (i < 0)
return (void *)addr;
addr_t zone_base = _zones[i];
addr_t offset = (addr_t)addr - zone_base;
return (void *)(_base + offset);
}
public:
/**
* Memory zone within Mem allocator
*/
class Zone_alloc : public Allocator
{
private:
Mem *_pool; /* pool of zone */
int _zone; /* zone number */
addr_t _base; /* base address of zone */
size_t _size; /* size of zone */
public:
Zone_alloc(Mem *pool, int zone, addr_t base, size_t size)
: _pool(pool), _zone(zone), _base(base), _size(size) { }
/*************************
** Alocator interface **
*************************/
bool alloc(size_t size, void **out_addr)
{
*out_addr = _pool->alloc(size, _zone);
if (!*out_addr) {
PERR("Zone of %zu bytes allocation failed", size);
return false;
}
return true;
}
void free(void *addr, size_t /* size */) { _pool->free(addr, _zone); }
size_t overhead(size_t size) { return 0; }
/**
* Check if address matches zone
*/
bool match(void const *addr)
{
addr_t a = (addr_t)addr;
bool ret = ((a >= _base) && (a < (_base + _size)));
return ret;
}
/**
* Retrieve virt to phys mapping
*/
addr_t phys_addr(void const *addr) { return _pool->phys_addr(addr, _zone); }
};
/**
* Gernal purpose memory pool
*/
static Mem* pool()
{
static Mem _p(MEM_POOL);
return &_p;
}
/**
* DMA memory pool
*/
static Mem* dma()
{
static Mem _p(DMA_POOL, false);
return &_p;
}
/**
* Allocator interface
*/
void *alloc(size_t size, int zone = -1, int align = 2)
{
void *addr;
if (!_range.alloc_aligned(size, &addr, align)) {
PERR("Memory allocation of %zu bytes failed", size);
return 0;
}
return _to_zone(addr, zone);
}
/**
* Free addr in zone
*/
void free(void *addr, int zone = -1) { _range.free(_from_zone(addr, zone)); }
/**
* Get phys for virt address
*/
addr_t phys_addr(void const *addr, int zone = - 1)
{
addr_t a = (addr_t)_from_zone(addr, zone);
if (a < _base || a >= _base + _size) {
PERR("No DMA phys addr for %lx zone: %d", a, zone);
return 0;
}
return (a - _base) + _base_phys;
}
/**
* Iinit allocator with count zones
*/
void init_zones(int count)
{
if (_zone_count)
return;
_zones = (addr_t *)env()->heap()->alloc(count * sizeof(addr_t));
_zone_count = count;
for (int i = 0; i < _zone_count; i++) {
_zones[i] = (addr_t)env()->rm_session()->attach(_ds_cap);
dde_kit_log(DEBUG_DMA, "Zone %d: base: %lx end %lx", i, _zones[i], _zones[i] + _size);
}
PINF("Registered %d zone allocators", count);
}
/**
* Create new zone allocator
*
* 'init_zones' must have been called beforehand
*/
Zone_alloc *new_zone_allocator()
{
if(_zone_alloc >= _zone_count) {
PERR("Zone allocators exhausted");
return 0;
}
Zone_alloc *zone = new(env()->heap()) Zone_alloc(this,
_zone_alloc,
_zones[_zone_alloc],
_size);
_zone_alloc++;
return zone;
}
};
}
#endif /* _MEM_H_ */