mirror of
https://github.com/genodelabs/genode.git
synced 2025-04-08 03:45:24 +00:00
block_tester: update to new block-client API
- Added 'io_buffer' attribute, default is 4M - Added 'batch' attribute, specifying the number of jobs used in parallel, default is 1 (sequential) - Removed 'synchronous' attribute (use batch of 1 instead) - Added 'copy' attribute (default "yes") - Print number of signals ("triggered") Issue #3283
This commit is contained in:
parent
784206c85c
commit
17fda73ca1
@ -37,13 +37,18 @@ install_config {
|
||||
<resource name="RAM" quantum="32M"/>
|
||||
<config verbose="yes" report="no" log="yes" stop_on_error="no">
|
||||
<tests>
|
||||
<sequential length="1M" size="4K" synchronous="yes"/>
|
||||
<sequential length="1M" size="8K" synchronous="yes"/>
|
||||
<sequential length="1M" size="16K"/>
|
||||
<sequential length="1M" size="64K"/>
|
||||
<sequential length="1M" size="128K"/>
|
||||
<sequential length="1M" size="4K" synchronous="yes" write="yes"/>
|
||||
<sequential length="1M" size="64K" write="yes" synchronous="yes"/>
|
||||
<sequential copy="no" length="16M" size="4K"/>
|
||||
<sequential copy="no" length="16M" size="8K"/>
|
||||
|
||||
<sequential copy="no" length="3G" size="16K" batch="32"/>
|
||||
<sequential copy="no" length="15" size="64K" batch="32"/>
|
||||
<sequential copy="no" length="30G" size="128K" batch="32"/>
|
||||
|
||||
<sequential copy="no" length="16M" size="4K" write="yes"/>
|
||||
<sequential copy="no" length="16M" size="64K" write="yes"/>
|
||||
|
||||
<!-- test client-side request splitting -->
|
||||
<sequential copy="no" length="16M" size="512K" io_buffer="64K"/>
|
||||
</tests>
|
||||
</config>
|
||||
<route>
|
||||
|
@ -81,21 +81,27 @@ append config {
|
||||
<resource name="RAM" quantum="32M"/>
|
||||
<config verbose="yes" report="no" log="yes" stop_on_error="no">
|
||||
<tests>
|
||||
<!-- synchronous="no" 4K/8K currently leads to deadlocking ahci_drv
|
||||
<sequential length="1G" size="4K"/>
|
||||
<sequential length="1G" size="8K"/>
|
||||
-->
|
||||
<sequential length="1G" size="4K" synchronous="yes"/>
|
||||
<sequential length="1G" size="8K" synchronous="yes"/>
|
||||
<sequential length="1G" size="16K"/>
|
||||
<sequential length="1G" size="64K"/>
|
||||
<sequential length="1G" size="128K"/>
|
||||
<sequential length="1G" size="4K" synchronous="yes" write="yes"/>
|
||||
<sequential length="1G" size="64K" write="yes" synchronous="yes"/>
|
||||
<random length="1G" size="16K" seed="0xdeadbeef" synchronous="yes"/>
|
||||
<random length="8G" size="512K" seed="0xc0ffee" synchronous="yes"/>
|
||||
<ping_pong length="1G" size="16K"/>
|
||||
<replay bulk="no">
|
||||
<!-- synchronous="no" 4K/8K currently leads to deadlocking ahci_drv -->
|
||||
|
||||
<sequential copy="no" length="128M" size="4K"/>
|
||||
<sequential copy="no" length="128M" size="4K" batch="32"/>
|
||||
<sequential copy="no" length="128M" size="4K" batch="1000"/>
|
||||
|
||||
<sequential copy="no" length="128M" size="8K"/>
|
||||
<sequential copy="no" length="128M" size="8K" batch="32"/>
|
||||
<sequential copy="no" length="1G" size="16K" batch="32"/>
|
||||
<sequential copy="no" length="1G" size="64K" batch="32"/>
|
||||
<sequential copy="no" length="1G" size="128K" batch="32"/>
|
||||
|
||||
<sequential copy="no" length="128M" size="4K" write="yes"/>
|
||||
<sequential copy="no" length="1G" size="64K" write="yes"/>
|
||||
|
||||
<random length="128M" size="16K" seed="0xdeadbeef" batch="32"/>
|
||||
<random length="512M" size="512K" seed="0xc0ffee" />
|
||||
|
||||
<ping_pong length="128M" size="16K"/>
|
||||
|
||||
<replay batch="10">
|
||||
<request type="read" lba="0" count="1"/>
|
||||
<request type="read" lba="0" count="1"/>
|
||||
<request type="read" lba="0" count="1"/>
|
||||
|
@ -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:
|
||||
! <!-- write 1200MiB in 8KiB requests -->
|
||||
! <sequential write="yes" length="1200M" size="8K"/>
|
||||
!
|
||||
! <!-- read first 32GiB in 128K chunks skipping 1MiB inbetween -->
|
||||
! <sequential start="0" length="32G" size="128K" skip="1M"/>
|
||||
!
|
||||
! <!-- replay the beginning Ext2 mount operations -->
|
||||
! <replay>
|
||||
! <request type="read" block_number="2" count="1"/>
|
||||
@ -166,13 +165,14 @@ value tuples:
|
||||
* rx:<int> number of blocks read
|
||||
* size:<int> size of one request in bytes
|
||||
* test:<string> name of the test
|
||||
* triggered<int> number of handled I/O signals
|
||||
* tx:<int> 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
|
||||
|
@ -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<Test_base>::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>;
|
||||
|
||||
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<Test_base>::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::Connection> _timer { };
|
||||
|
||||
Constructible<Timer::Periodic_timeout<Test_base>> _progress_timeout { };
|
||||
|
||||
Allocator_avl _block_alloc { &_alloc };
|
||||
|
||||
struct Job;
|
||||
typedef Block::Connection<Job> Block_connection;
|
||||
|
||||
Constructible<Block_connection> _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<Test_base> _block_io_sigh {
|
||||
_env.ep(), *this, &Test_base::_handle_block_io };
|
||||
|
||||
public:
|
||||
|
||||
friend class Genode::Fifo<Test_base>;
|
||||
|
||||
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 <test_ping_pong.h>
|
||||
@ -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;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -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::Connection<>> _block { };
|
||||
using Test_base::Test_base;
|
||||
|
||||
Genode::Constructible<Timer::Connection> _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<Timer::Periodic_timeout<Ping_pong>> _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<Ping_pong> _ack_sigh {
|
||||
_env.ep(), *this, &Ping_pong::_handle_ack };
|
||||
|
||||
Genode::Signal_handler<Ping_pong> _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_ */
|
||||
|
@ -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::Connection<>> _block { };
|
||||
|
||||
Genode::Constructible<Timer::Connection> _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<Timer::Periodic_timeout<Random>> _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<Random> _ack_sigh {
|
||||
_env.ep(), *this, &Random::_handle_ack };
|
||||
|
||||
Genode::Signal_handler<Random> _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 <typename... ARGS>
|
||||
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_ */
|
||||
|
@ -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<Request>::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<Request> requests { };
|
||||
|
||||
char _scratch_buffer[4u<<20] { };
|
||||
|
||||
bool _bulk { false };
|
||||
|
||||
Genode::Constructible<Timer::Connection> _timer { };
|
||||
|
||||
enum { TX_BUF_SIZE = 4 * 1024 * 1024, };
|
||||
Genode::Allocator_avl _block_alloc { &alloc };
|
||||
Genode::Constructible<Block::Connection<>> _block { };
|
||||
|
||||
Genode::Signal_handler<Replay> _ack_sigh {
|
||||
env.ep(), *this, &Replay::_handle_ack };
|
||||
|
||||
Genode::Signal_handler<Replay> _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<bool>("verbose", false);
|
||||
_bulk = config.attribute_value<bool>("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_ */
|
||||
|
@ -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::Connection<>> _block { };
|
||||
|
||||
Genode::Constructible<Timer::Connection> _timer { };
|
||||
|
||||
/* test data */
|
||||
size_t _blocks { 0 };
|
||||
size_t _ack_blocks { 0 };
|
||||
char _scratch_buffer[1u<<20] { };
|
||||
|
||||
Genode::Constructible<Timer::Periodic_timeout<Sequential>> _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<Sequential> _ack_sigh {
|
||||
_env.ep(), *this, &Sequential::_handle_ack };
|
||||
|
||||
Genode::Signal_handler<Sequential> _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_ */
|
||||
|
Loading…
x
Reference in New Issue
Block a user