From 68c1b8675cd865f98308ca0f09d0f5784128d3f9 Mon Sep 17 00:00:00 2001 From: Emery Hemingway Date: Sun, 18 Mar 2018 11:52:59 +0100 Subject: [PATCH] gems: magic ring buffer A ring buffer that uses a single dataspace mapped twice in consecutive regions. This allows any operation that is less or equal to the size of the buffer to be read or written in a single pass. The capacity of Magic_ring_buffer is defined at runtime. Fix #2725 --- repos/gems/include/gems/magic_ring_buffer.h | 155 ++++++++++++++++++ repos/gems/recipes/api/gems/hash | 2 +- repos/gems/run/magic_ring_buffer.run | 29 ++++ repos/gems/src/test/magic_ring_buffer/main.cc | 57 +++++++ .../gems/src/test/magic_ring_buffer/target.mk | 3 + 5 files changed, 245 insertions(+), 1 deletion(-) create mode 100644 repos/gems/include/gems/magic_ring_buffer.h create mode 100644 repos/gems/run/magic_ring_buffer.run create mode 100644 repos/gems/src/test/magic_ring_buffer/main.cc create mode 100644 repos/gems/src/test/magic_ring_buffer/target.mk diff --git a/repos/gems/include/gems/magic_ring_buffer.h b/repos/gems/include/gems/magic_ring_buffer.h new file mode 100644 index 000000000..5153e43a6 --- /dev/null +++ b/repos/gems/include/gems/magic_ring_buffer.h @@ -0,0 +1,155 @@ +/* + * \brief Region magic ring buffer + * \author Emery Hemingway + * \date 2018-02-01 + */ + +/* + * Copyright (C) 2018 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 _INCLUDE__GEMS__RING_BUFFER_H_ +#define _INCLUDE__GEMS__RING_BUFFER_H_ + +/* Genode includes */ +#include +#include +#include +#include + +namespace Genode { + template + struct Magic_ring_buffer; +} + +/** + * A ring buffer that uses a single dataspace mapped twice in consecutive + * regions. This allows any operation that is less or equal to the size of + * the buffer to be read or written in a single pass. + */ +template +class Genode::Magic_ring_buffer +{ + private: + + Magic_ring_buffer(Magic_ring_buffer const &); + Magic_ring_buffer &operator = (Magic_ring_buffer const &); + + Genode::Env &_env; + + Ram_dataspace_capability _buffer_ds; + + size_t const _ds_size = Dataspace_client(_buffer_ds).size(); + size_t const _capacity = _ds_size / sizeof(TYPE); + + Rm_connection _rm_connection { _env }; + + /* create region map (reserve address space) */ + Region_map_client _rm { _rm_connection.create(_ds_size*2) }; + + /* attach map to global region map */ + TYPE *_buffer = (TYPE *)_env.rm().attach(_rm.dataspace()); + + size_t _wpos = 0; + size_t _rpos = 0; + + + public: + + /** + * Ring capacity of TYPE items + */ + size_t capacity() { return _capacity; } + + /** + * Constructor + * + * \param TYPE Ring item type, size of type must be a + * power of two and less than the page size + * + * \param env Env for dataspace allocation and mapping + * \param num_bytes Size of ring in bytes, may be rounded up + * to the next page boundry + * + * \throw Region_map::Region_conflict + * \throw Out_of_ram + * \throw Out_of_caps + * + */ + Magic_ring_buffer(Genode::Env &env, size_t num_bytes) + : _env(env), _buffer_ds(_env.pd().alloc(num_bytes)) + { + if (_ds_size % sizeof(TYPE)) { + error("Magic_ring_buffer cannot hold unaligned TYPE"); + throw Exception(); + } + + /* attach buffer dataspace twice into reserved region */ + _rm.attach_at(_buffer_ds, 0, _ds_size); + _rm.attach_at(_buffer_ds, _ds_size, _ds_size); + } + + ~Magic_ring_buffer() + { + /* detach dataspace from reserved region */ + _rm.detach((addr_t)_ds_size); + _rm.detach((addr_t)0); + + /* detach reserved region */ + _env.rm().detach((addr_t)_buffer); + + /* free buffer */ + _env.ram().free(_buffer_ds); + } + + /** + * Number of items that may be written to ring + */ + size_t write_avail() const + { + if (_wpos > _rpos) + return ((_rpos - _wpos + _capacity) % _capacity) - 2; + else if (_wpos < _rpos) + return _rpos - _wpos; + else + return _capacity - 2; + } + + /** + * Number of items that may be read from ring + */ + size_t read_avail() const + { + if (_wpos > _rpos) + return _wpos - _rpos; + else + return (_wpos - _rpos + _capacity) % _capacity; + } + + /** + * Pointer to ring write address + */ + TYPE *write_addr() const { return &_buffer[_wpos]; } + + /** + * Pointer to ring read address + */ + TYPE *read_addr() const { return &_buffer[_rpos]; } + + /** + * Advance the ring write pointer + */ + void fill(size_t items) { + _wpos = (_wpos+items) % _capacity; } + + /** + * Advance the ring read pointer + */ + void drain(size_t items) { + _rpos = (_rpos+items) % _capacity; } +}; + +#endif diff --git a/repos/gems/recipes/api/gems/hash b/repos/gems/recipes/api/gems/hash index efd8eda4a..3cb2c3c24 100644 --- a/repos/gems/recipes/api/gems/hash +++ b/repos/gems/recipes/api/gems/hash @@ -1 +1 @@ -2018-01-28 b383e364411d3ee377e40ad8ab19e77f9b4c9503 +2018-03-18 47dfc20e9d2493c3596be2c6c0d9fdcdd38ef7b2 diff --git a/repos/gems/run/magic_ring_buffer.run b/repos/gems/run/magic_ring_buffer.run new file mode 100644 index 000000000..4c9d59cab --- /dev/null +++ b/repos/gems/run/magic_ring_buffer.run @@ -0,0 +1,29 @@ +create_boot_directory + +install_config { + + + + + + + + + + + + + + + +} + +build "core init test/magic_ring_buffer" + +build_boot_image { + core ld.lib.so init test-magic_ring_buffer +} + +append qemu_args " -nographic " + +run_genode_until "child .* exited with exit value 0.*\n" 10 diff --git a/repos/gems/src/test/magic_ring_buffer/main.cc b/repos/gems/src/test/magic_ring_buffer/main.cc new file mode 100644 index 000000000..deb94bdff --- /dev/null +++ b/repos/gems/src/test/magic_ring_buffer/main.cc @@ -0,0 +1,57 @@ +/* + * \brief Magic ring buffer test + * \author Emery Hemingway + * \date 2018-04-04 + */ + +/* + * Copyright (C) 2018 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. + */ + +/* Genode includes */ +#include +#include + +/* gems includes */ +#include + +void Component::construct(Genode::Env &env) +{ + using namespace Genode; + + Magic_ring_buffer ring_buffer(env, 4097); + + log("--- magic ring buffer test, ", ring_buffer.capacity(), " int ring ---"); + + + int const count = ring_buffer.capacity()/3; + + int total = 0; + + for (int j = 0; j < 99; ++j) { + for (int i = 0; i < count; ++i) { + ring_buffer.write_addr()[i] = i; + } + + ring_buffer.fill(count); + + for (int i = 0; i < count; ++i) { + if (ring_buffer.read_addr()[i] != i) { + error("ring buffer corruption, ", + ring_buffer.read_addr()[i], " != ", i); + env.parent().exit(total+i); + return; + } + } + ring_buffer.drain(count); + + total += count; + } + + log("--- test complete, ", total, " ints passed through ring ---"); + env.parent().exit(0); +} + diff --git a/repos/gems/src/test/magic_ring_buffer/target.mk b/repos/gems/src/test/magic_ring_buffer/target.mk new file mode 100644 index 000000000..716d9fb7b --- /dev/null +++ b/repos/gems/src/test/magic_ring_buffer/target.mk @@ -0,0 +1,3 @@ +TARGET = test-magic_ring_buffer +SRC_CC = main.cc +LIBS = base