lx_emul: implement lookup of dma-addr to virt-addr

* Use the new Lx_kit::Map as lookup structure for virt-to-dma and vice versa,
  instead of a list-based registry

Ref #4268
This commit is contained in:
Stefan Kalkowski 2021-09-27 11:41:14 +02:00 committed by Norman Feske
parent 9bc7ecb605
commit f8cf0442ed
4 changed files with 87 additions and 38 deletions

View File

@ -23,6 +23,7 @@ void * lx_emul_mem_alloc_uncached(unsigned long size);
void * lx_emul_mem_alloc_aligned(unsigned long size, unsigned long align);
void * lx_emul_mem_alloc_aligned_uncached(unsigned long size, unsigned long align);
unsigned long lx_emul_mem_dma_addr(void * addr);
unsigned long lx_emul_mem_virt_addr(void * dma_addr);
void lx_emul_mem_free(const void * ptr);
unsigned long lx_emul_mem_size(const void * ptr);
void lx_emul_mem_cache_clean_invalidate(const void * ptr, unsigned long size);

View File

@ -18,7 +18,8 @@
#include <base/cache.h>
#include <base/env.h>
#include <base/heap.h>
#include <base/registry.h>
#include <lx_kit/byte_range.h>
#include <lx_kit/map.h>
namespace Platform { class Connection; };
namespace Lx_kit {
@ -31,7 +32,7 @@ class Lx_kit::Mem_allocator
{
private:
class Buffer : Interface
class Buffer
{
private:
@ -45,18 +46,56 @@ class Lx_kit::Mem_allocator
addr_t dma_addr)
: _ds(rm, cap), _dma_addr(dma_addr) {}
addr_t dma_addr() const { return _dma_addr; }
Attached_dataspace & ds() { return _ds; }
size_t dma_addr() const { return _dma_addr; }
size_t size() const { return _ds.size(); }
size_t virt_addr() const {
return (addr_t) _ds.local_addr<void*>(); }
Attached_dataspace & ds() { return _ds; }
};
using Buffer_registry = Registry<Registered<Buffer>>;
struct Buffer_info
{
struct Key { addr_t addr; } key;
Buffer & buffer;
size_t size() const { return buffer.size(); }
bool higher(Key const other_key) const
{
return key.addr > other_key.addr;
}
struct Query_range
{
addr_t addr;
size_t size;
bool matches(Buffer_info const & bi) const
{
Lx_kit::Byte_range buf_range { bi.key.addr, bi.size() };
Lx_kit::Byte_range range { addr, size };
return buf_range.intersects(range);
}
Key key() const { return Key { addr }; }
};
struct Query_addr : Query_range
{
Query_addr(void const * addr)
: Query_range{(addr_t)addr, 1} { }
};
};
Env & _env;
Heap & _heap;
Platform::Connection & _platform;
Cache _cache_attr;
Allocator_avl _mem;
Buffer_registry _buffers {};
Map<Buffer_info> _virt_to_dma { _heap };
Map<Buffer_info> _dma_to_virt { _heap };
public:
@ -69,8 +108,8 @@ class Lx_kit::Mem_allocator
void * alloc(size_t size, size_t align);
addr_t dma_addr(void * addr);
addr_t virt_addr(void * dma_addr);
size_t size(const void * ptr);
void free(Attached_dataspace * ds);
bool free(const void * ptr);
};

View File

@ -64,6 +64,17 @@ extern "C" unsigned long lx_emul_mem_dma_addr(void * addr)
}
extern "C" unsigned long lx_emul_mem_virt_addr(void * dma_addr)
{
unsigned long ret = Lx_kit::env().memory.virt_addr(dma_addr);
if (ret)
return ret;
if (!(ret = Lx_kit::env().uncached_memory.virt_addr(dma_addr)))
Genode::error(__func__, " called with invalid addr ", dma_addr);
return ret;
}
extern "C" void lx_emul_mem_free(const void * ptr)
{
if (!ptr)

View File

@ -19,6 +19,8 @@
/* local includes */
#include <lx_kit/memory.h>
#include <lx_kit/map.h>
#include <lx_kit/byte_range.h>
Genode::Attached_dataspace & Lx_kit::Mem_allocator::alloc_dataspace(size_t size)
@ -26,17 +28,18 @@ Genode::Attached_dataspace & Lx_kit::Mem_allocator::alloc_dataspace(size_t size)
Ram_dataspace_capability ds_cap;
try {
size_t ds_size = align_addr(size, 12);
ds_cap = _platform.alloc_dma_buffer(ds_size, _cache_attr);
addr_t dma_addr = _platform.dma_addr(ds_cap);
Ram_dataspace_capability ds_cap =
_platform.alloc_dma_buffer(align_addr(size, 12), _cache_attr);
Buffer & buffer = *new (_heap)
Registered<Buffer>(_buffers, _env.rm(), ds_cap, dma_addr);
addr_t addr = (addr_t)buffer.ds().local_addr<void>();
Buffer(_env.rm(), ds_cap, _platform.dma_addr(ds_cap));
/* map eager by touching all pages once */
for (size_t sz = 0; sz < ds_size; sz += 4096) {
touch_read((unsigned char const volatile*)(addr + sz)); }
for (size_t sz = 0; sz < buffer.size(); sz += 4096) {
touch_read((unsigned char const volatile*)(buffer.virt_addr() + sz)); }
_virt_to_dma.insert(buffer.virt_addr(), buffer);
_dma_to_virt.insert(buffer.dma_addr(), buffer);
return buffer.ds();
} catch (Out_of_caps) {
@ -70,7 +73,8 @@ void * Lx_kit::Mem_allocator::alloc(size_t size, size_t align)
* and physical addresses of a multi-page allocation are always
* contiguous.
*/
Attached_dataspace & ds = alloc_dataspace(max(size + 1, min_buffer_size));
Attached_dataspace & ds = alloc_dataspace(max(size + 1,
min_buffer_size));
_mem.add_range((addr_t)ds.local_addr<void>(), ds.size() - 1);
@ -93,15 +97,24 @@ Genode::addr_t Lx_kit::Mem_allocator::dma_addr(void * addr)
{
addr_t ret = 0UL;
_buffers.for_each([&] (Buffer & b) {
addr_t other = (addr_t)addr;
addr_t addr = (addr_t)b.ds().local_addr<void>();
if (addr > other || (addr+b.ds().size()) <= other)
return;
_virt_to_dma.apply(Buffer_info::Query_addr(addr),
[&] (Buffer_info const & info) {
addr_t const offset = (addr_t)addr - info.buffer.virt_addr();
ret = info.buffer.dma_addr() + offset;
});
/* byte offset of 'addr' from start of block */
addr_t const offset = other - addr;
ret = b.dma_addr() + offset;
return ret;
}
Genode::addr_t Lx_kit::Mem_allocator::virt_addr(void * dma_addr)
{
addr_t ret = 0UL;
_dma_to_virt.apply(Buffer_info::Query_addr(dma_addr),
[&] (Buffer_info const & info) {
addr_t const offset = (addr_t)dma_addr - info.buffer.dma_addr();
ret = info.buffer.virt_addr() + offset;
});
return ret;
@ -118,21 +131,6 @@ bool Lx_kit::Mem_allocator::free(const void * ptr)
}
void Lx_kit::Mem_allocator::free(Attached_dataspace * ds)
{
Dataspace_capability cap = ds->cap();
Registered<Buffer> * buffer = nullptr;
_buffers.for_each([&] (Buffer & b) {
if (&b.ds() == ds)
buffer = static_cast<Registered<Buffer>*>(&b);
});
destroy(_heap, buffer);
_platform.free_dma_buffer(static_cap_cast<Ram_dataspace>(cap));
}
Genode::size_t Lx_kit::Mem_allocator::size(const void * ptr)
{
return ptr ? _mem.size_at(ptr) : 0;