diff --git a/repos/os/run/block_request_stream.run b/repos/os/run/block_request_stream.run
index 2b4d8b44e6..673de36b7d 100644
--- a/repos/os/run/block_request_stream.run
+++ b/repos/os/run/block_request_stream.run
@@ -37,13 +37,18 @@ install_config {
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/repos/os/run/block_tester.run b/repos/os/run/block_tester.run
index 85b239eef2..fcabd63d4c 100644
--- a/repos/os/run/block_tester.run
+++ b/repos/os/run/block_tester.run
@@ -81,21 +81,27 @@ append config {
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/repos/os/src/app/block_tester/README b/repos/os/src/app/block_tester/README
index 7e5b5e78b4..e41ecb9f61 100644
--- a/repos/os/src/app/block_tester/README
+++ b/repos/os/src/app/block_tester/README
@@ -20,9 +20,6 @@ The following list contains all available tests:
* 'replay' executes a previously recorded Block session operation sequence.
- - If the 'bulk' attribute is specified, the test will try to fill up
- the packet stream with request until it is full.
-
Each request is specified by a 'request' node which has the following
attributes:
@@ -50,15 +47,8 @@ The following list contains all available tests:
- The 'write' attribute specifies whether the tests writes or reads, if
it is missing it defaults to reading.
- - The 'skip' attribute specifies how many bytes should be skipped between
- each request.
-
- - The 'synchronous' attribute specifies if each request is done in a
- synchronous fashion or if the component tries to fill up the transfer
- buffer as much as possible.
-
* 'ping_pong' reads or writes Blocks from the beginning and the end of the
- specfied part of the Block session in an alternating fashion
+ specified part of the Block session in an alternating fashion
- The 'start' attribute specifies the logical block address where the test
begins, if it is missing the first block is used.
@@ -90,6 +80,18 @@ which are supported by every test:
- If the 'verbose' attribute is specified, the test will print each
request to the LOG session before it will be executed.
+ - The 'copy' attribute specifies whether the content should be copied
+ in memory, which a typical client would do. The default is "yes".
+ If set to "no", the payload data remains untouched, exposing the raw
+ I/O and protocol overhead.
+
+ - The 'batch' attribute specifies how many block-operation jobs are
+ issued at once. The default value is 1, which corresponds to a
+ sequential mode of operation.
+
+ - The 'io_buffer' attribute defines the size of the I/O communication
+ buffer for the block session. The default value is "4M".
+
Note: all tests use a fixed sized scratch buffer of 1 (replay 4) MiB, plan the
quota and request size accordingly.
@@ -116,9 +118,6 @@ The following config snippet illustrates the use of the component:
!
!
!
-!
-!
-!
!
!
!
@@ -166,13 +165,14 @@ value tuples:
* rx: number of blocks read
* size: size of one request in bytes
* test: name of the test
+ * triggered number of handled I/O signals
* tx: number of blocks written
Since the LOG output is mainly intended for automated testing and analyzing all
size values are given in bytes. The following examplary output illustrates the
structure:
-! test:sequential rx:1048576 tx:0 bytes:536870912 size:65536 bsize:512 duration:302 mibs:1695.364 iops:27125.828 result:0
+! finished sequential rx:32768 tx:0 bytes:134217728 size:131072 bsize:4096 duration:27 mibs:4740.740 iops:37925.925 triggered:35 result:ok
Report
diff --git a/repos/os/src/app/block_tester/main.cc b/repos/os/src/app/block_tester/main.cc
index 797dc2a944..88197803ae 100644
--- a/repos/os/src/app/block_tester/main.cc
+++ b/repos/os/src/app/block_tester/main.cc
@@ -26,118 +26,285 @@ namespace Test {
using namespace Genode;
- /*
- * Result of a test
- */
- struct Result
- {
- uint64_t duration { 0 };
- uint64_t bytes { 0 };
- uint64_t rx { 0 };
- uint64_t tx { 0 };
- uint64_t request_size { 0 };
- uint64_t block_size { 0 };
- bool success { false };
-
- bool calculate { false };
- float mibs { 0.0f };
- float iops { 0.0f };
-
- Result() { }
-
- Result(bool success, uint64_t d, uint64_t b, uint64_t rx, uint64_t tx,
- uint64_t rsize, uint64_t bsize)
- :
- duration(d), bytes(b), rx(rx), tx(tx),
- request_size(rsize ? rsize : bsize), block_size(bsize),
- success(success)
- {
- mibs = ((double)bytes / ((double)duration/1000)) / (1024 * 1024);
- /* total ops / seconds w/o any latency inclusion */
- iops = (double)((rx + tx) / (request_size / block_size))
- / ((double)duration / 1000);
- }
-
- void print(Genode::Output &out) const
- {
- Genode::print(out, "rx:", rx, " ",
- "tx:", tx, " ",
- "bytes:", bytes, " ",
- "size:", request_size, " ",
- "bsize:", block_size, " ",
- "duration:", duration, " "
- );
-
- if (calculate) {
- Genode::print(out, "mibs:", (unsigned)(mibs * (1<<20u)),
- " ", "iops:", (unsigned)(iops + 0.5f));
- }
-
- Genode::print(out, " result:", success ? "ok" : "failed");
- }
- };
-
- /*
- * Base class used for test running list
- */
- struct Test_base : private Genode::Fifo::Element
- {
- protected:
-
- /*
- * Must be called by every test when it has finished
- */
- Genode::Signal_context_capability _finished_sig;
-
- void _finish()
- {
- _finished = true;
- if (_finished_sig.valid()) {
- Genode::Signal_transmitter(_finished_sig).submit();
- }
- }
-
- bool _verbose { false };
-
- Block::Session::Info _info { };
-
- size_t _length_in_blocks { 0 };
- size_t _size_in_blocks { 0 };
-
- uint64_t _start_time { 0 };
- uint64_t _end_time { 0 };
- size_t _bytes { 0 };
- uint64_t _rx { 0 };
- uint64_t _tx { 0 };
-
- bool _stop_on_error { true };
- bool _finished { false };
- bool _success { false };
-
- public:
-
- friend class Genode::Fifo;
-
- Test_base(Genode::Signal_context_capability finished_sig)
- : _finished_sig(finished_sig) { }
-
- virtual ~Test_base() { };
-
- /********************
- ** Test interface **
- ********************/
-
- virtual void start(bool stop_on_error) = 0;
- virtual Result finish() = 0;
- virtual char const *name() const = 0;
- };
-
- struct Test_failed : Genode::Exception { };
- struct Constructing_test_failed : Genode::Exception { };
-
+ struct Result;
+ struct Test_base;
struct Main;
-} /* Test */
+ struct Test_failed : Exception { };
+ struct Constructing_test_failed : Exception { };
+}
+
+
+struct Test::Result
+{
+ uint64_t duration { 0 };
+ uint64_t bytes { 0 };
+ uint64_t rx { 0 };
+ uint64_t tx { 0 };
+ uint64_t request_size { 0 };
+ uint64_t block_size { 0 };
+ size_t triggered { 0 };
+ bool success { false };
+
+ bool calculate { false };
+ float mibs { 0.0f };
+ float iops { 0.0f };
+
+ Result() { }
+
+ Result(bool success, uint64_t d, uint64_t b, uint64_t rx, uint64_t tx,
+ uint64_t rsize, uint64_t bsize, size_t triggered)
+ :
+ duration(d), bytes(b), rx(rx), tx(tx),
+ request_size(rsize ? rsize : bsize), block_size(bsize),
+ triggered(triggered), success(success)
+ {
+ mibs = ((double)bytes / ((double)duration/1000)) / (1024 * 1024);
+ /* total ops / seconds w/o any latency inclusion */
+ iops = (double)((rx + tx) / (request_size / block_size))
+ / ((double)duration / 1000);
+ }
+
+ void print(Genode::Output &out) const
+ {
+ Genode::print(out, "rx:", rx, " ",
+ "tx:", tx, " ",
+ "bytes:", bytes, " ",
+ "size:", request_size, " ",
+ "bsize:", block_size, " ",
+ "duration:", duration, " "
+ );
+
+ if (calculate) {
+ Genode::print(out, "mibs:", mibs, " iops:", iops);
+ }
+
+ Genode::print(out, " triggered:", triggered);
+ Genode::print(out, " result:", success ? "ok" : "failed");
+ }
+};
+
+
+/*
+ * Base class used for test running list
+ */
+struct Test::Test_base : private Genode::Fifo::Element
+{
+ protected:
+
+ Env &_env;
+ Allocator &_alloc;
+
+ Xml_node const _node;
+
+ typedef Block::block_number_t block_number_t;
+
+ bool const _verbose;
+ size_t const _io_buffer;
+ uint64_t const _progress_interval;
+ bool const _copy;
+ size_t const _batch;
+
+ Constructible _timer { };
+
+ Constructible> _progress_timeout { };
+
+ Allocator_avl _block_alloc { &_alloc };
+
+ struct Job;
+ typedef Block::Connection Block_connection;
+
+ Constructible _block { };
+
+ struct Job : Block_connection::Job
+ {
+ unsigned const id;
+
+ Job(Block_connection &connection, Block::Operation operation, unsigned id)
+ :
+ Block_connection::Job(connection, operation), id(id)
+ { }
+ };
+
+ /*
+ * Must be called by every test when it has finished
+ */
+ Genode::Signal_context_capability _finished_sig;
+
+ void finish()
+ {
+ _end_time = _timer->elapsed_ms();
+
+ _finished = true;
+ if (_finished_sig.valid()) {
+ Genode::Signal_transmitter(_finished_sig).submit();
+ }
+
+ _timer.destruct();
+ }
+
+ Block::Session::Info _info { };
+
+ size_t _length_in_blocks { 0 };
+ size_t _size_in_blocks { 0 };
+
+ uint64_t _start_time { 0 };
+ uint64_t _end_time { 0 };
+ size_t _bytes { 0 };
+ uint64_t _rx { 0 };
+ uint64_t _tx { 0 };
+ size_t _triggered { 0 }; /* number of I/O signals */
+ unsigned _job_cnt { 0 };
+ unsigned _completed { 0 };
+
+ bool _stop_on_error { true };
+ bool _finished { false };
+ bool _success { false };
+
+ char _scratch_buffer[1u<<20] { };
+
+ void _memcpy(char *dst, char const *src, size_t length)
+ {
+ if (length > sizeof(_scratch_buffer)) {
+ warning("scratch buffer too small for copying");
+ return;
+ }
+
+ Genode::memcpy(dst, src, length);
+ }
+
+ public:
+
+ /**
+ * Block::Connection::Update_jobs_policy
+ */
+ void produce_write_content(Job &job, off_t offset, char *dst, size_t length)
+ {
+ _tx += length / _info.block_size;
+ _bytes += length;
+
+ if (_verbose)
+ log("job ", job.id, ": writing ", length, " bytes at ", offset);
+
+ if (_copy)
+ _memcpy(dst, _scratch_buffer, length);
+ }
+
+ /**
+ * Block::Connection::Update_jobs_policy
+ */
+ void consume_read_result(Job &job, off_t offset,
+ char const *src, size_t length)
+ {
+ _rx += length / _info.block_size;
+ _bytes += length;
+
+ if (_verbose)
+ log("job ", job.id, ": got ", length, " bytes at ", offset);
+
+ if (_copy)
+ _memcpy(_scratch_buffer, src, length);
+ }
+
+ /**
+ * Block_connection::Update_jobs_policy
+ */
+ void completed(Job &job, bool success)
+ {
+ _completed++;
+
+ if (_verbose)
+ log("job ", job.id, ": ", job.operation(), ", completed");
+
+ if (!success)
+ error("processing ", job.operation(), " failed");
+
+ destroy(_alloc, &job);
+
+ if (!success && _stop_on_error)
+ throw Test_failed();
+
+ /* replace completed job by new one */
+ _spawn_job();
+
+ bool const jobs_active = (_job_cnt != _completed);
+
+ _success = !jobs_active && success;
+
+ if (!jobs_active || !success)
+ finish();
+ }
+
+ protected:
+
+ void _handle_progress_timeout(Duration)
+ {
+ log("progress: rx:", _rx, " tx:", _tx);
+ }
+
+ void _handle_block_io()
+ {
+ _triggered++;
+ _block->update_jobs(*this);
+ }
+
+ Signal_handler _block_io_sigh {
+ _env.ep(), *this, &Test_base::_handle_block_io };
+
+ public:
+
+ friend class Genode::Fifo;
+
+ Test_base(Env &env, Allocator &alloc, Xml_node node,
+ Signal_context_capability finished_sig)
+ :
+ _env(env), _alloc(alloc), _node(node),
+ _verbose(node.attribute_value("verbose", false)),
+ _io_buffer(_node.attribute_value("io_buffer",
+ Number_of_bytes(4*1024*1024))),
+ _progress_interval(_node.attribute_value("progress", 0ul)),
+ _copy(_node.attribute_value("copy", true)),
+ _batch(_node.attribute_value("batch", 1u)),
+ _finished_sig(finished_sig)
+ {
+ if (_progress_interval)
+ _progress_timeout.construct(*_timer, *this,
+ &Test_base::_handle_progress_timeout,
+ Microseconds(_progress_interval*1000));
+ }
+
+ virtual ~Test_base() { };
+
+ void start(bool stop_on_error)
+ {
+ _stop_on_error = stop_on_error;
+
+ _block.construct(_env, &_block_alloc, _io_buffer);
+ _block->sigh(_block_io_sigh);
+ _info = _block->info();
+
+ _init();
+
+ for (unsigned i = 0; i < _batch; i++)
+ _spawn_job();
+
+ _timer.construct(_env);
+ _start_time = _timer->elapsed_ms();
+
+ _handle_block_io();
+ }
+
+ /********************
+ ** Test interface **
+ ********************/
+
+ virtual void _init() = 0;
+ virtual void _spawn_job() = 0;
+ virtual Result result() = 0;
+ virtual char const *name() const = 0;
+ virtual void print(Output &) const = 0;
+};
+
/* tests */
#include
@@ -156,9 +323,6 @@ struct Test::Main
Genode::Attached_rom_dataspace _config_rom { _env, "config" };
- bool const _verbose {
- _config_rom.xml().attribute_value("verbose", false) };
-
bool const _log {
_config_rom.xml().attribute_value("log", false) };
@@ -219,14 +383,14 @@ struct Test::Main
{
/* clean up current test */
if (_current) {
- Result r = _current->finish();
+ Result r = _current->result();
if (!r.success) { _success = false; }
r.calculate = _calculate;
if (_log) {
- Genode::log("test:", _current->name(), " ", r);
+ Genode::log("finished ", _current->name(), " ", r);
}
if (_report) {
@@ -237,9 +401,6 @@ struct Test::Main
_generate_report();
}
- if (_verbose) {
- Genode::log("finished ", _current->name(), " ", r.success ? 0 : 1);
- }
Genode::destroy(&_heap, _current);
_current = nullptr;
}
@@ -247,13 +408,14 @@ struct Test::Main
/* execute next test */
if (!_current) {
_tests.dequeue([&] (Test_base &head) {
- if (_verbose) { Genode::log("start ", head.name()); }
+ if (_log) { Genode::log("start ", head); }
try {
head.start(_stop_on_error);
_current = &head;
} catch (...) {
- Genode::log("Could not start ", head.name());
+ Genode::log("Could not start ", head);
Genode::destroy(&_heap, &head);
+ throw;
}
});
}
diff --git a/repos/os/src/app/block_tester/test_ping_pong.h b/repos/os/src/app/block_tester/test_ping_pong.h
index 67ce9bbe92..ff9341a4ad 100644
--- a/repos/os/src/app/block_tester/test_ping_pong.h
+++ b/repos/os/src/app/block_tester/test_ping_pong.h
@@ -14,9 +14,7 @@
#ifndef _TEST_PING_PONG_H_
#define _TEST_PING_PONG_H_
-namespace Test {
- struct Ping_pong;
-}
+namespace Test { struct Ping_pong; }
/*
@@ -27,200 +25,77 @@ namespace Test {
*/
struct Test::Ping_pong : Test_base
{
- Genode::Env &_env;
- Genode::Allocator &_alloc;
+ bool _ping = true;
+ block_number_t _end = 0;
+ block_number_t _start = _node.attribute_value("start", 0u);
+ size_t const _size = _node.attribute_value("size", Number_of_bytes());
+ size_t const _length = _node.attribute_value("length", Number_of_bytes());
- Block::Packet_descriptor::Opcode _op {
- Block::Packet_descriptor::READ };
+ Block::Operation::Type const _op_type = _node.attribute_value("write", false)
+ ? Block::Operation::Type::WRITE
+ : Block::Operation::Type::READ;
- /* block session infos */
- enum { TX_BUF_SIZE = 4 * 1024 * 1024, };
- Genode::Allocator_avl _block_alloc { &_alloc };
- Genode::Constructible> _block { };
+ using Test_base::Test_base;
- Genode::Constructible _timer { };
-
- /* test data */
- bool _ping { true };
- Block::sector_t _start { 0 };
- Block::sector_t _end { 0 };
- size_t _length { 0 };
- size_t _size { 0 };
-
- size_t _blocks { 0 };
- char _scratch_buffer[1u<<20] { };
-
- Genode::Constructible> _progress_timeout { };
-
- void _handle_progress_timeout(Genode::Duration)
+ void _spawn_job() override
{
- Genode::log("progress: rx:", _rx, " tx:", _tx);
- }
-
- void _handle_submit()
- {
- try {
- while (_blocks < _length_in_blocks && _block->tx()->ready_to_submit()) {
-
- Block::Packet_descriptor tmp =
- _block->alloc_packet(_size_in_blocks * _info.block_size);
-
- Block::sector_t const lba = _ping ? _start + _blocks
- : _end - _blocks;
- _ping ^= true;
-
- Block::Packet_descriptor p(tmp,
- _op, lba, _size_in_blocks);
-
- /* simulate write */
- if (_op == Block::Packet_descriptor::WRITE) {
- char * const content = _block->tx()->packet_content(p);
- Genode::memcpy(content, _scratch_buffer, p.size());
- }
-
- _block->tx()->submit_packet(p);
- _blocks += _size_in_blocks;
- }
- } catch (...) { }
- }
-
- void _handle_ack()
- {
- if (_finished) { return; }
-
- while (_block->tx()->ack_avail()) {
-
- Block::Packet_descriptor p = _block->tx()->get_acked_packet();
-
- if (!p.succeeded()) {
- Genode::error("processing ", p.block_number(), " ",
- p.block_count(), " failed");
-
- if (_stop_on_error) { throw Test_failed(); }
- else { _finish(); }
- }
-
- size_t const psize = p.size();
- size_t const count = psize / _info.block_size;
- Block::Packet_descriptor::Opcode const op = p.operation();
-
- /* simulate read */
- if (op == Block::Packet_descriptor::READ) {
- char * const content = _block->tx()->packet_content(p);
- Genode::memcpy(_scratch_buffer, content, p.size());
- }
-
- _rx += (op == Block::Packet_descriptor::READ) * count;
- _tx += (op == Block::Packet_descriptor::WRITE) * count;
-
- _bytes += psize;
- _block->tx()->release_packet(p);
- }
-
- if (_bytes >= _length) {
- _success = true;
- _finish();
+ if (_bytes >= _length)
return;
- }
- _handle_submit();
+ _job_cnt++;
+
+ block_number_t const lba = _ping ? _start : _end - _start;
+ _ping = !_ping;
+
+ Block::Operation const operation { .type = _op_type,
+ .block_number = lba,
+ .count = _size_in_blocks };
+
+ new (_alloc) Job(*_block, operation, _job_cnt);
+
+ _start += _size_in_blocks;
}
- void _finish()
+ void _init() override
{
- _end_time = _timer->elapsed_ms();
-
- Test_base::_finish();
- }
-
- Genode::Signal_handler _ack_sigh {
- _env.ep(), *this, &Ping_pong::_handle_ack };
-
- Genode::Signal_handler _submit_sigh {
- _env.ep(), *this, &Ping_pong::_handle_submit };
-
- Genode::Xml_node _node;
-
- /**
- * Constructor
- *
- * \param block Block session reference
- * \param node node containing the test configuration
- */
- Ping_pong(Genode::Env &env, Genode::Allocator &alloc,
- Genode::Xml_node node,
- Genode::Signal_context_capability finished_sig)
- : Test_base(finished_sig), _env(env), _alloc(alloc), _node(node)
- { }
-
- /********************
- ** Test interface **
- ********************/
-
- void start(bool stop_on_error) override
- {
- _stop_on_error = stop_on_error;
-
- _block.construct(_env, &_block_alloc, TX_BUF_SIZE);
-
- _block->tx_channel()->sigh_ack_avail(_ack_sigh);
- _block->tx_channel()->sigh_ready_to_submit(_submit_sigh);
-
- _info = _block->info();
- _start = _node.attribute_value("start", 0u);
- _size = _node.attribute_value("size", Number_of_bytes());
- _length = _node.attribute_value("length", Number_of_bytes());
-
if (_size > sizeof(_scratch_buffer)) {
- Genode::error("request size exceeds scratch buffer size");
+ error("request size exceeds scratch buffer size");
throw Constructing_test_failed();
}
size_t const total_bytes = _info.block_count * _info.block_size;
if (_length > total_bytes - (_start * _info.block_size)) {
- Genode::error("length too large invalid");
+ error("length too large invalid");
throw Constructing_test_failed();
}
if (_info.block_size > _size || (_size % _info.block_size) != 0) {
- Genode::error("request size invalid");
+ error("request size invalid");
throw Constructing_test_failed();
}
- if (_node.attribute_value("write", false)) {
- _op = Block::Packet_descriptor::WRITE;
- }
-
_size_in_blocks = _size / _info.block_size;
_length_in_blocks = _length / _info.block_size;
_end = _start + _length_in_blocks;
-
- _timer.construct(_env);
-
- uint64_t const progress_interval = _node.attribute_value("progress", 0ul);
- if (progress_interval) {
- _progress_timeout.construct(*_timer, *this,
- &Ping_pong::_handle_progress_timeout,
- Genode::Microseconds(progress_interval*1000));
- }
-
- _start_time = _timer->elapsed_ms();
- _handle_submit();
}
- Result finish() override
+ Result result() override
{
- _timer.destruct();
- _block.destruct();
-
return Result(_success, _end_time - _start_time,
- _bytes, _rx, _tx, _size, _info.block_size);
+ _bytes, _rx, _tx, _size, _info.block_size, _triggered);
}
char const *name() const override { return "ping_pong"; }
+
+ void print(Output &out) const override
+ {
+ Genode::print(out, name(), " ", Block::Operation::type_name(_op_type), " "
+ "start:", _start, " "
+ "size:", _size, " "
+ "length:", _length, " "
+ "copy:", _copy, " "
+ "batch:", _batch);
+ }
};
-
-
-
#endif /* _TEST_PING_PONG_H_ */
diff --git a/repos/os/src/app/block_tester/test_random.h b/repos/os/src/app/block_tester/test_random.h
index 72f34475db..8ebf1cb705 100644
--- a/repos/os/src/app/block_tester/test_random.h
+++ b/repos/os/src/app/block_tester/test_random.h
@@ -14,9 +14,8 @@
#ifndef _TEST_RANDOM_H_
#define _TEST_RANDOM_H_
-namespace Test {
- struct Random;
-}
+namespace Test { struct Random; }
+
namespace Util {
@@ -79,236 +78,101 @@ namespace Util {
*/
struct Test::Random : Test_base
{
- Genode::Env &_env;
- Genode::Allocator &_alloc;
-
- Block::Packet_descriptor::Opcode _op {
- Block::Packet_descriptor::READ };
bool _alternate_access { false };
- /* block session infos */
- enum { TX_BUF_SIZE = 4 * 1024 * 1024, };
- Genode::Allocator_avl _block_alloc { &_alloc };
- Genode::Constructible> _block { };
-
- Genode::Constructible _timer { };
-
Util::Xoroshiro _random;
- size_t _size { 0 };
- uint64_t _length { 0 };
- char _scratch_buffer[1u<<20] { };
+ size_t const _size = _node.attribute_value("size", Number_of_bytes());
+ uint64_t const _length = _node.attribute_value("length", Number_of_bytes());
- size_t _blocks { 0 };
+ Block::Operation::Type _op_type = Block::Operation::Type::READ;
- /* _synchronous controls bulk */
- bool _synchronous { false };
-
- Genode::Constructible> _progress_timeout { };
-
- void _handle_progress_timeout(Genode::Duration)
- {
- Genode::log("progress: rx:", _rx, " tx:", _tx);
- }
-
- Block::sector_t _next_block()
+ block_number_t _next_block()
{
uint64_t r = 0;
+ block_number_t max = _info.block_count;
+ if (max >= _size_in_blocks + 1)
+ max -= _size_in_blocks + 1;
do {
- r = _random.get() % _info.block_count;
+ r = _random.get() % max;
} while (r + _size_in_blocks > _info.block_count);
return r;
}
- void _handle_submit()
- {
- try {
- bool next = true;
- while (_blocks < _length_in_blocks && _block->tx()->ready_to_submit() && next) {
-
- Block::Packet_descriptor tmp = _block->alloc_packet(_size);
-
- Block::sector_t lba = _next_block();
-
- Block::Packet_descriptor::Opcode op =
- _alternate_access ? (lba & 0x1)
- ? Block::Packet_descriptor::WRITE
- : Block::Packet_descriptor::READ
- : _op;
-
- Block::Packet_descriptor p(tmp, op, lba, _size_in_blocks);
-
- bool const write = op == Block::Packet_descriptor::WRITE;
-
- /* simulate write */
- if (write) {
- char * const content = _block->tx()->packet_content(p);
- Genode::memcpy(content, _scratch_buffer, p.size());
- }
-
- if (_verbose) {
- Genode::log("submit: lba:", lba, " size:", _size,
- " ", write ? "tx" : "rx");
- }
-
- _block->tx()->submit_packet(p);
- _blocks += _size_in_blocks;
-
- next = !_synchronous;
- }
- } catch (...) { }
- }
-
- void _handle_ack()
- {
- if (_finished) { return; }
-
- while (_block->tx()->ack_avail()) {
-
- Block::Packet_descriptor p = _block->tx()->get_acked_packet();
-
- if (!p.succeeded()) {
- Genode::error("processing ", p.block_number(), " ",
- p.block_count(), " failed");
-
- if (_stop_on_error) { throw Test_failed(); }
- else { _finish(); break; }
- }
-
- size_t const psize = p.size();
- size_t const count = psize / _info.block_size;
- Block::Packet_descriptor::Opcode const op = p.operation();
-
- bool const read = op == Block::Packet_descriptor::READ;
-
- /* simulate read */
- if (read) {
- char * const content = _block->tx()->packet_content(p);
- Genode::memcpy(_scratch_buffer, content, p.size());
- }
-
- if (_verbose) {
- Genode::log("ack: lba:", p.block_number(), " size:", p.size(),
- " ", read ? "rx" : "tx");
- }
-
- _rx += (op == Block::Packet_descriptor::READ) * count;
- _tx += (op == Block::Packet_descriptor::WRITE) * count;
-
- _bytes += psize;
- _block->tx()->release_packet(p);
- }
-
- if (_bytes >= _length) {
- _success = true;
- _finish();
- return;
- }
-
- _handle_submit();
- }
-
- void _finish()
- {
- _end_time = _timer->elapsed_ms();
-
- Test_base::_finish();
- }
-
- Genode::Signal_handler _ack_sigh {
- _env.ep(), *this, &Random::_handle_ack };
-
- Genode::Signal_handler _submit_sigh {
- _env.ep(), *this, &Random::_handle_submit };
-
- Genode::Xml_node _node;
-
- /**
- * Constructor
- *
- * \param block Block session reference
- * \param node node containing the test configuration
- */
- Random(Genode::Env &env, Genode::Allocator &alloc,
- Genode::Xml_node node,
- Genode::Signal_context_capability finished_sig)
+ template
+ Random(ARGS &&...args)
:
- Test_base(finished_sig), _env(env), _alloc(alloc),
- _random(node.attribute_value("seed", 42u)),
- _node(node)
+ Test_base(args...),
+ _random(_node.attribute_value("seed", 42UL))
+ { }
+
+ void _init() override
{
- _verbose = node.attribute_value("verbose", false);
- }
-
- /********************
- ** Test interface **
- ********************/
-
- void start(bool stop_on_error) override
- {
- _stop_on_error = stop_on_error;
-
- _block.construct(_env, &_block_alloc, TX_BUF_SIZE);
-
- _block->tx_channel()->sigh_ack_avail(_ack_sigh);
- _block->tx_channel()->sigh_ready_to_submit(_submit_sigh);
-
- _info = _block->info();
- _size = _node.attribute_value("size", Number_of_bytes());
- _length = _node.attribute_value("length", Number_of_bytes());
-
if (_size > sizeof(_scratch_buffer)) {
- Genode::error("request size exceeds scratch buffer size");
+ error("request size exceeds scratch buffer size");
throw Constructing_test_failed();
}
if (!_size || !_length) {
- Genode::error("request size or length invalid");
+ error("request size or length invalid");
throw Constructing_test_failed();
}
if (_info.block_size > _size || (_size % _info.block_size) != 0) {
- Genode::error("request size invalid ", _info.block_size, " ", _size);
+ error("request size invalid ", _info.block_size, " ", _size);
throw Constructing_test_failed();
}
- _synchronous = _node.attribute_value("synchronous", false);
+ bool const r = _node.attribute_value("read", false);
+ if (r) { _op_type = Block::Operation::Type::READ; }
- bool r = _node.attribute_value("write", false);
- if (r) { _op = Block::Packet_descriptor::WRITE; }
-
- bool w = _node.attribute_value("read", false);
- if (w) { _op = Block::Packet_descriptor::WRITE; }
+ bool const w = _node.attribute_value("write", false);
+ if (w) { _op_type = Block::Operation::Type::WRITE; }
_alternate_access = w && r;
_size_in_blocks = _size / _info.block_size;
_length_in_blocks = _length / _info.block_size;
-
- _timer.construct(_env);
-
- uint64_t const progress_interval = _node.attribute_value("progress", 0ul);
- if (progress_interval) {
- _progress_timeout.construct(*_timer, *this,
- &Random::_handle_progress_timeout,
- Genode::Microseconds(progress_interval*1000));
- }
-
- _start_time = _timer->elapsed_ms();
- _handle_submit();
}
- Result finish() override
+ void _spawn_job() override
{
- _timer.destruct();
- _block.destruct();
+ if (_bytes >= _length)
+ return;
+ _job_cnt++;
+
+ block_number_t const lba = _next_block();
+
+ Block::Operation::Type const op_type =
+ _alternate_access ? (lba & 0x1) ? Block::Operation::Type::WRITE
+ : Block::Operation::Type::READ
+ : _op_type;
+
+ Block::Operation const operation { .type = op_type,
+ .block_number = lba,
+ .count = _size_in_blocks };
+
+ new (_alloc) Job(*_block, operation, _job_cnt);
+ }
+
+ Result result() override
+ {
return Result(_success, _end_time - _start_time,
- _bytes, _rx, _tx, _size, _info.block_size);
+ _bytes, _rx, _tx, _size, _info.block_size, _triggered);
}
char const *name() const override { return "random"; }
+
+ void print(Output &out) const override
+ {
+ Genode::print(out, name(), " "
+ "size:", _size, " "
+ "length:", _length, " "
+ "copy:", _copy, " "
+ "batch:", _batch);
+ }
};
#endif /* _TEST_RANDOM_H_ */
diff --git a/repos/os/src/app/block_tester/test_replay.h b/repos/os/src/app/block_tester/test_replay.h
index c9a5fdbed4..a6ea71ad42 100644
--- a/repos/os/src/app/block_tester/test_replay.h
+++ b/repos/os/src/app/block_tester/test_replay.h
@@ -14,194 +14,68 @@
#ifndef _TEST_REPLAY_H_
#define _TEST_REPLAY_H_
-namespace Test {
- struct Replay;
-}
+namespace Test { struct Replay; }
/*
* Replay test
*
- * This test replays a recorded sequence of Block session
- * requests.
+ * This test replays a recorded sequence of Block session requests.
*/
struct Test::Replay : Test_base
{
- Genode::Env &env;
- Genode::Allocator &alloc;
+ using Test_base::Test_base;
- struct Request : Genode::Fifo::Element
+ void _init() override
{
- Block::Packet_descriptor::Opcode op;
- Block::sector_t nr;
- Genode::size_t count;
-
- Request(Block::Packet_descriptor::Opcode op,
- Block::sector_t nr, Genode::size_t count)
- : op(op), nr(nr), count(count) { }
- };
-
- unsigned request_num { 0 };
- Genode::Fifo requests { };
-
- char _scratch_buffer[4u<<20] { };
-
- bool _bulk { false };
-
- Genode::Constructible _timer { };
-
- enum { TX_BUF_SIZE = 4 * 1024 * 1024, };
- Genode::Allocator_avl _block_alloc { &alloc };
- Genode::Constructible> _block { };
-
- Genode::Signal_handler _ack_sigh {
- env.ep(), *this, &Replay::_handle_ack };
-
- Genode::Signal_handler _submit_sigh {
- env.ep(), *this, &Replay::_handle_submit };
-
- void _handle_submit()
- {
- bool more = true;
-
try {
- while (_block->tx()->ready_to_submit() && more) {
- /* peak at head ... */
- more = false;
- requests.dequeue([&] (Request &req) {
- Block::Packet_descriptor p(
- _block->alloc_packet(req.count * _info.block_size),
- req.op, req.nr, req.count);
+ _node.for_each_sub_node("request", [&](Xml_node request) {
- bool const write = req.op == Block::Packet_descriptor::WRITE;
+ auto op_type = [&] ()
+ {
+ typedef String<8> Type;
- /* simulate write */
- if (write) {
- char * const content = _block->tx()->packet_content(p);
- Genode::memcpy(content, _scratch_buffer, p.size());
- }
+ if (request.attribute_value("type", Type()) == "read")
+ return Block::Operation::Type::READ;
- _block->tx()->submit_packet(p);
+ if (request.attribute_value("type", Type()) == "write")
+ return Block::Operation::Type::WRITE;
- Genode::destroy(&alloc, &req);
- more = _bulk;
- });
- }
- } catch (...) { }
- }
+ error("operation type not defined: ", request);
+ throw 1;
+ };
- void _finish()
- {
- _end_time = _timer->elapsed_ms();
+ Block::Operation const operation
+ {
+ .type = op_type(),
+ .block_number = request.attribute_value("lba", (block_number_t)0),
+ .count = request.attribute_value("count", 0UL)
+ };
- Test_base::_finish();
- }
-
- void _handle_ack()
- {
- if (_finished) { return; }
-
- while (_block->tx()->ack_avail()) {
-
- Block::Packet_descriptor p = _block->tx()->get_acked_packet();
- if (!p.succeeded()) {
- Genode::error("packet failed lba: ", p.block_number(),
- " count: ", p.block_count());
-
- if (_stop_on_error) { throw Test_failed(); }
- else { _finish(); }
- } else {
-
- /* simulate read */
- if (p.operation() == Block::Packet_descriptor::READ) {
- char * const content = _block->tx()->packet_content(p);
- Genode::memcpy(_scratch_buffer, content, p.size());
- }
-
- size_t const psize = p.size();
- size_t const count = psize / _info.block_size;
-
- _rx += (p.operation() == Block::Packet_descriptor::READ) * count;
- _tx += (p.operation() == Block::Packet_descriptor::WRITE) * count;
-
- _bytes += psize;
-
- if (--request_num == 0) {
- _success = true;
- _finish();
- }
- }
-
- _block->tx()->release_packet(p);
- }
-
- _handle_submit();
- }
-
- Replay(Genode::Env &env, Genode::Allocator &alloc,
- Genode::Xml_node config,
- Genode::Signal_context_capability finished_sig)
- : Test_base(finished_sig), env(env), alloc(alloc)
- {
- _verbose = config.attribute_value("verbose", false);
- _bulk = config.attribute_value("bulk", false);
-
- try {
- config.for_each_sub_node("request", [&](Xml_node request) {
-
- typedef Genode::String<8> Type;
-
- Block::sector_t const nr = request.attribute_value("lba", (Block::sector_t)0u);
- Genode::size_t const count = request.attribute_value("count", 0UL);
- Type const type = request.attribute_value("type", Type());
-
- Block::Packet_descriptor::Opcode op;
- if (type == "read") { op = Block::Packet_descriptor::READ; }
- else if (type == "write") { op = Block::Packet_descriptor::WRITE; }
- else { throw -1; }
-
- requests.enqueue(*(new (&alloc) Request(op, nr, count)));
- ++request_num;
+ _job_cnt++;
+ new (&_alloc) Job(*_block, operation, _job_cnt);
});
} catch (...) {
- Genode::error("could not read request list");
+ error("could not read request list");
+
+ _block->dissolve_all_jobs([&] (Job &job) { destroy(_alloc, &job); });
return;
}
}
- /********************
- ** Test interface **
- ********************/
+ void _spawn_job() override { }
- void start(bool stop_on_error) override
+ Result result() override
{
- _stop_on_error = stop_on_error;
-
- _block.construct(env, &_block_alloc, TX_BUF_SIZE);
-
- _block->tx_channel()->sigh_ack_avail(_ack_sigh);
- _block->tx_channel()->sigh_ready_to_submit(_submit_sigh);
-
- _info = _block->info();
-
- _timer.construct(env);
-
- _start_time = _timer->elapsed_ms();
-
- _handle_submit();
- }
-
- Result finish() override
- {
- _timer.destruct();
- _block.destruct();
-
return Result(_success, _end_time - _start_time,
- _bytes, _rx, _tx, 0u, _info.block_size);
+ _bytes, _rx, _tx, 0u, _info.block_size, _triggered);
}
char const *name() const override { return "replay"; }
+
+ void print(Output &out) const override
+ {
+ Genode::print(out, name());
+ }
};
-
-
#endif /* _TEST_REPLAY_H_ */
diff --git a/repos/os/src/app/block_tester/test_sequential.h b/repos/os/src/app/block_tester/test_sequential.h
index b4b474ab2a..a16c00ed10 100644
--- a/repos/os/src/app/block_tester/test_sequential.h
+++ b/repos/os/src/app/block_tester/test_sequential.h
@@ -14,9 +14,7 @@
#ifndef _TEST_SEQUENTIAL_H_
#define _TEST_SEQUENTIAL_H_
-namespace Test {
- struct Sequential;
-}
+namespace Test { struct Sequential; }
/*
@@ -27,218 +25,65 @@ namespace Test {
*/
struct Test::Sequential : Test_base
{
- Genode::Env &_env;
- Genode::Allocator &_alloc;
+ block_number_t _start = _node.attribute_value("start", 0u);
+ size_t const _size = _node.attribute_value("size", Number_of_bytes());
+ size_t const _length = _node.attribute_value("length", Number_of_bytes());
- /* test infos */
- Block::sector_t _start { 0 };
- size_t _length { 0 };
- size_t _size { 0 };
+ Block::Operation::Type const _op_type = _node.attribute_value("write", false)
+ ? Block::Operation::Type::WRITE
+ : Block::Operation::Type::READ;
- /* _synchronous controls bulk */
- bool _synchronous { false };
+ using Test_base::Test_base;
- Block::Packet_descriptor::Opcode _op {
- Block::Packet_descriptor::READ };
-
- /* block session infos */
- enum { TX_BUF_SIZE = 4 * 1024 * 1024, };
- Genode::Allocator_avl _block_alloc { &_alloc };
- Genode::Constructible> _block { };
-
- Genode::Constructible _timer { };
-
- /* test data */
- size_t _blocks { 0 };
- size_t _ack_blocks { 0 };
- char _scratch_buffer[1u<<20] { };
-
- Genode::Constructible> _progress_timeout { };
-
- void _handle_progress_timeout(Genode::Duration)
+ void _init() override
{
- Genode::log("progress: rx:", _rx, " tx:", _tx);
- }
-
- void _handle_submit()
- {
- try {
- bool next = true;
- while (_blocks < _length_in_blocks && _block->tx()->ready_to_submit() && next) {
-
- Block::Packet_descriptor tmp = _block->alloc_packet(_size);
-
- Block::Packet_descriptor p(tmp,
- _op, _start, _size_in_blocks);
-
- bool const write = _op == Block::Packet_descriptor::WRITE;
-
- /* simulate write */
- if (write) {
- char * const content = _block->tx()->packet_content(p);
- Genode::memcpy(content, _scratch_buffer, p.size());
- }
-
- try { _block->tx()->submit_packet(p); }
- catch (...) { _block->tx()->release_packet(p); }
-
- if (_verbose) {
- Genode::log("submit: lba:", _start, " size:", _size,
- " ", write ? "tx" : "rx");
- }
-
- _start += _size_in_blocks;
- _blocks += _size_in_blocks;
-
- /* wrap if needed */
- if (_start >= _info.block_count) { _start = 0; }
-
- next = !_synchronous;
- }
- } catch (...) { }
- }
-
- void _handle_ack()
- {
- if (_finished) { return; }
-
- while (_block->tx()->ack_avail()) {
-
- Block::Packet_descriptor p = _block->tx()->get_acked_packet();
-
- if (!p.succeeded()) {
- Genode::error("processing ", p.block_number(), " ",
- p.block_count(), " failed");
-
- if (_stop_on_error) { throw Test_failed(); }
- else { _finish(); break; }
- }
-
- bool const read = _op == Block::Packet_descriptor::READ;
-
- /* simulate read */
- if (read) {
- char * const content = _block->tx()->packet_content(p);
- Genode::memcpy(_scratch_buffer, content, p.size());
- }
-
- size_t const psize = p.size();
- size_t const count = psize / _info.block_size;
- Block::Packet_descriptor::Opcode const op = p.operation();
-
- _rx += (op == Block::Packet_descriptor::READ) * count;
- _tx += (op == Block::Packet_descriptor::WRITE) * count;
-
- _bytes += psize;
- _ack_blocks += count;
-
- if (_verbose) {
- Genode::log("ack: lba:", p.block_number(), " size:", p.size(),
- " ", read ? "rx" : "tx");
- }
-
- _block->tx()->release_packet(p);
- }
-
- if (_bytes >= _length || _ack_blocks == _length_in_blocks) {
- _success = true;
- _finish();
- return;
- }
-
- _handle_submit();
- }
-
- void _finish()
- {
- _end_time = _timer->elapsed_ms();
-
- Test_base::_finish();
- }
-
- Genode::Signal_handler _ack_sigh {
- _env.ep(), *this, &Sequential::_handle_ack };
-
- Genode::Signal_handler _submit_sigh {
- _env.ep(), *this, &Sequential::_handle_submit };
-
- Genode::Xml_node _node;
-
- /**
- * Constructor
- *
- * \param block Block session reference
- * \param node node containing the test configuration
- */
- Sequential(Genode::Env &env, Genode::Allocator &alloc,
- Genode::Xml_node node,
- Genode::Signal_context_capability finished_sig)
- : Test_base(finished_sig), _env(env), _alloc(alloc), _node(node)
- {
- _verbose = node.attribute_value("verbose", false);
- }
-
- /********************
- ** Test interface **
- ********************/
-
- void start(bool stop_on_error) override
- {
- _stop_on_error = stop_on_error;
-
- _block.construct(_env, &_block_alloc, TX_BUF_SIZE);
-
- _block->tx_channel()->sigh_ack_avail(_ack_sigh);
- _block->tx_channel()->sigh_ready_to_submit(_submit_sigh);
-
- _info = _block->info();
-
- _synchronous = _node.attribute_value("synchronous", false);
-
- _start = _node.attribute_value("start", 0u);
- _size = _node.attribute_value("size", Genode::Number_of_bytes());
- _length = _node.attribute_value("length", Genode::Number_of_bytes());
-
if (_size > sizeof(_scratch_buffer)) {
- Genode::error("request size exceeds scratch buffer size");
+ error("request size exceeds scratch buffer size");
throw Constructing_test_failed();
}
if (_info.block_size > _size || (_size % _info.block_size) != 0) {
- Genode::error("request size invalid");
+ error("request size invalid");
throw Constructing_test_failed();
}
- if (_node.attribute_value("write", false)) {
- _op = Block::Packet_descriptor::WRITE;
- }
-
_size_in_blocks = _size / _info.block_size;
_length_in_blocks = _length / _info.block_size;
-
- _timer.construct(_env);
-
- uint64_t const progress_interval = _node.attribute_value("progress", 0ul);
- if (progress_interval) {
- _progress_timeout.construct(*_timer, *this,
- &Sequential::_handle_progress_timeout,
- Genode::Microseconds(progress_interval*1000));
- }
-
- _start_time = _timer->elapsed_ms();
- _handle_submit();
}
- Result finish() override
+ void _spawn_job() override
{
- _timer.destruct();
- _block.destruct();
+ if (_bytes >= _length)
+ return;
+ _job_cnt++;
+
+ Block::Operation const operation { .type = _op_type,
+ .block_number = _start,
+ .count = _size_in_blocks };
+
+ new (_alloc) Job(*_block, operation, _job_cnt);
+
+ _start += _size_in_blocks;
+ }
+
+ Result result() override
+ {
return Result(_success, _end_time - _start_time,
- _bytes, _rx, _tx, _size, _info.block_size);
+ _bytes, _rx, _tx, _size, _info.block_size, _triggered);
}
char const *name() const override { return "sequential"; }
+
+ void print(Genode::Output &out) const override
+ {
+ Genode::print(out, name(), " ", Block::Operation::type_name(_op_type), " "
+ "start:", _start, " "
+ "size:", _size, " "
+ "length:", _length, " "
+ "copy:", _copy, " "
+ "batch:", _batch);
+ }
};
#endif /* _TEST_SEQUENTIAL_H_ */