diff --git a/repos/os/run/block_request_stream.run b/repos/os/run/block_request_stream.run index 2b4d8b44e..673de36b7 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 85b239eef..fcabd63d4 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 7e5b5e78b..e41ecb9f6 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 797dc2a94..88197803a 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 67ce9bbe9..ff9341a4a 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 72f34475d..8ebf1cb70 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 c9a5fdbed..a6ea71ad4 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 b4b474ab2..a16c00ed1 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_ */