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"/>
|
<resource name="RAM" quantum="32M"/>
|
||||||
<config verbose="yes" report="no" log="yes" stop_on_error="no">
|
<config verbose="yes" report="no" log="yes" stop_on_error="no">
|
||||||
<tests>
|
<tests>
|
||||||
<sequential length="1M" size="4K" synchronous="yes"/>
|
<sequential copy="no" length="16M" size="4K"/>
|
||||||
<sequential length="1M" size="8K" synchronous="yes"/>
|
<sequential copy="no" length="16M" size="8K"/>
|
||||||
<sequential length="1M" size="16K"/>
|
|
||||||
<sequential length="1M" size="64K"/>
|
<sequential copy="no" length="3G" size="16K" batch="32"/>
|
||||||
<sequential length="1M" size="128K"/>
|
<sequential copy="no" length="15" size="64K" batch="32"/>
|
||||||
<sequential length="1M" size="4K" synchronous="yes" write="yes"/>
|
<sequential copy="no" length="30G" size="128K" batch="32"/>
|
||||||
<sequential length="1M" size="64K" write="yes" synchronous="yes"/>
|
|
||||||
|
<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>
|
</tests>
|
||||||
</config>
|
</config>
|
||||||
<route>
|
<route>
|
||||||
|
|
|
@ -81,21 +81,27 @@ append config {
|
||||||
<resource name="RAM" quantum="32M"/>
|
<resource name="RAM" quantum="32M"/>
|
||||||
<config verbose="yes" report="no" log="yes" stop_on_error="no">
|
<config verbose="yes" report="no" log="yes" stop_on_error="no">
|
||||||
<tests>
|
<tests>
|
||||||
<!-- synchronous="no" 4K/8K currently leads to deadlocking ahci_drv
|
<!-- synchronous="no" 4K/8K currently leads to deadlocking ahci_drv -->
|
||||||
<sequential length="1G" size="4K"/>
|
|
||||||
<sequential length="1G" size="8K"/>
|
<sequential copy="no" length="128M" size="4K"/>
|
||||||
-->
|
<sequential copy="no" length="128M" size="4K" batch="32"/>
|
||||||
<sequential length="1G" size="4K" synchronous="yes"/>
|
<sequential copy="no" length="128M" size="4K" batch="1000"/>
|
||||||
<sequential length="1G" size="8K" synchronous="yes"/>
|
|
||||||
<sequential length="1G" size="16K"/>
|
<sequential copy="no" length="128M" size="8K"/>
|
||||||
<sequential length="1G" size="64K"/>
|
<sequential copy="no" length="128M" size="8K" batch="32"/>
|
||||||
<sequential length="1G" size="128K"/>
|
<sequential copy="no" length="1G" size="16K" batch="32"/>
|
||||||
<sequential length="1G" size="4K" synchronous="yes" write="yes"/>
|
<sequential copy="no" length="1G" size="64K" batch="32"/>
|
||||||
<sequential length="1G" size="64K" write="yes" synchronous="yes"/>
|
<sequential copy="no" length="1G" size="128K" batch="32"/>
|
||||||
<random length="1G" size="16K" seed="0xdeadbeef" synchronous="yes"/>
|
|
||||||
<random length="8G" size="512K" seed="0xc0ffee" synchronous="yes"/>
|
<sequential copy="no" length="128M" size="4K" write="yes"/>
|
||||||
<ping_pong length="1G" size="16K"/>
|
<sequential copy="no" length="1G" size="64K" write="yes"/>
|
||||||
<replay bulk="no">
|
|
||||||
|
<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"/>
|
<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.
|
* '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
|
Each request is specified by a 'request' node which has the following
|
||||||
attributes:
|
attributes:
|
||||||
|
|
||||||
|
@ -50,15 +47,8 @@ The following list contains all available tests:
|
||||||
- The 'write' attribute specifies whether the tests writes or reads, if
|
- The 'write' attribute specifies whether the tests writes or reads, if
|
||||||
it is missing it defaults to reading.
|
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
|
* '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
|
- The 'start' attribute specifies the logical block address where the test
|
||||||
begins, if it is missing the first block is used.
|
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
|
- If the 'verbose' attribute is specified, the test will print each
|
||||||
request to the LOG session before it will be executed.
|
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
|
Note: all tests use a fixed sized scratch buffer of 1 (replay 4) MiB, plan the
|
||||||
quota and request size accordingly.
|
quota and request size accordingly.
|
||||||
|
|
||||||
|
@ -116,9 +118,6 @@ The following config snippet illustrates the use of the component:
|
||||||
! <!-- write 1200MiB in 8KiB requests -->
|
! <!-- write 1200MiB in 8KiB requests -->
|
||||||
! <sequential write="yes" length="1200M" size="8K"/>
|
! <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 the beginning Ext2 mount operations -->
|
||||||
! <replay>
|
! <replay>
|
||||||
! <request type="read" block_number="2" count="1"/>
|
! <request type="read" block_number="2" count="1"/>
|
||||||
|
@ -166,13 +165,14 @@ value tuples:
|
||||||
* rx:<int> number of blocks read
|
* rx:<int> number of blocks read
|
||||||
* size:<int> size of one request in bytes
|
* size:<int> size of one request in bytes
|
||||||
* test:<string> name of the test
|
* test:<string> name of the test
|
||||||
|
* triggered<int> number of handled I/O signals
|
||||||
* tx:<int> number of blocks written
|
* tx:<int> number of blocks written
|
||||||
|
|
||||||
Since the LOG output is mainly intended for automated testing and analyzing all
|
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
|
size values are given in bytes. The following examplary output illustrates the
|
||||||
structure:
|
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
|
Report
|
||||||
|
|
|
@ -26,118 +26,285 @@ namespace Test {
|
||||||
|
|
||||||
using namespace Genode;
|
using namespace Genode;
|
||||||
|
|
||||||
/*
|
struct Result;
|
||||||
* Result of a test
|
struct Test_base;
|
||||||
*/
|
|
||||||
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 Main;
|
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 */
|
/* tests */
|
||||||
#include <test_ping_pong.h>
|
#include <test_ping_pong.h>
|
||||||
|
@ -156,9 +323,6 @@ struct Test::Main
|
||||||
|
|
||||||
Genode::Attached_rom_dataspace _config_rom { _env, "config" };
|
Genode::Attached_rom_dataspace _config_rom { _env, "config" };
|
||||||
|
|
||||||
bool const _verbose {
|
|
||||||
_config_rom.xml().attribute_value("verbose", false) };
|
|
||||||
|
|
||||||
bool const _log {
|
bool const _log {
|
||||||
_config_rom.xml().attribute_value("log", false) };
|
_config_rom.xml().attribute_value("log", false) };
|
||||||
|
|
||||||
|
@ -219,14 +383,14 @@ struct Test::Main
|
||||||
{
|
{
|
||||||
/* clean up current test */
|
/* clean up current test */
|
||||||
if (_current) {
|
if (_current) {
|
||||||
Result r = _current->finish();
|
Result r = _current->result();
|
||||||
|
|
||||||
if (!r.success) { _success = false; }
|
if (!r.success) { _success = false; }
|
||||||
|
|
||||||
r.calculate = _calculate;
|
r.calculate = _calculate;
|
||||||
|
|
||||||
if (_log) {
|
if (_log) {
|
||||||
Genode::log("test:", _current->name(), " ", r);
|
Genode::log("finished ", _current->name(), " ", r);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_report) {
|
if (_report) {
|
||||||
|
@ -237,9 +401,6 @@ struct Test::Main
|
||||||
_generate_report();
|
_generate_report();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_verbose) {
|
|
||||||
Genode::log("finished ", _current->name(), " ", r.success ? 0 : 1);
|
|
||||||
}
|
|
||||||
Genode::destroy(&_heap, _current);
|
Genode::destroy(&_heap, _current);
|
||||||
_current = nullptr;
|
_current = nullptr;
|
||||||
}
|
}
|
||||||
|
@ -247,13 +408,14 @@ struct Test::Main
|
||||||
/* execute next test */
|
/* execute next test */
|
||||||
if (!_current) {
|
if (!_current) {
|
||||||
_tests.dequeue([&] (Test_base &head) {
|
_tests.dequeue([&] (Test_base &head) {
|
||||||
if (_verbose) { Genode::log("start ", head.name()); }
|
if (_log) { Genode::log("start ", head); }
|
||||||
try {
|
try {
|
||||||
head.start(_stop_on_error);
|
head.start(_stop_on_error);
|
||||||
_current = &head;
|
_current = &head;
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
Genode::log("Could not start ", head.name());
|
Genode::log("Could not start ", head);
|
||||||
Genode::destroy(&_heap, &head);
|
Genode::destroy(&_heap, &head);
|
||||||
|
throw;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,9 +14,7 @@
|
||||||
#ifndef _TEST_PING_PONG_H_
|
#ifndef _TEST_PING_PONG_H_
|
||||||
#define _TEST_PING_PONG_H_
|
#define _TEST_PING_PONG_H_
|
||||||
|
|
||||||
namespace Test {
|
namespace Test { struct Ping_pong; }
|
||||||
struct Ping_pong;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -27,200 +25,77 @@ namespace Test {
|
||||||
*/
|
*/
|
||||||
struct Test::Ping_pong : Test_base
|
struct Test::Ping_pong : Test_base
|
||||||
{
|
{
|
||||||
Genode::Env &_env;
|
bool _ping = true;
|
||||||
Genode::Allocator &_alloc;
|
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::Operation::Type const _op_type = _node.attribute_value("write", false)
|
||||||
Block::Packet_descriptor::READ };
|
? Block::Operation::Type::WRITE
|
||||||
|
: Block::Operation::Type::READ;
|
||||||
|
|
||||||
/* block session infos */
|
using Test_base::Test_base;
|
||||||
enum { TX_BUF_SIZE = 4 * 1024 * 1024, };
|
|
||||||
Genode::Allocator_avl _block_alloc { &_alloc };
|
|
||||||
Genode::Constructible<Block::Connection<>> _block { };
|
|
||||||
|
|
||||||
Genode::Constructible<Timer::Connection> _timer { };
|
void _spawn_job() override
|
||||||
|
|
||||||
/* 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)
|
|
||||||
{
|
{
|
||||||
Genode::log("progress: rx:", _rx, " tx:", _tx);
|
if (_bytes >= _length)
|
||||||
}
|
|
||||||
|
|
||||||
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();
|
|
||||||
return;
|
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)) {
|
if (_size > sizeof(_scratch_buffer)) {
|
||||||
Genode::error("request size exceeds scratch buffer size");
|
error("request size exceeds scratch buffer size");
|
||||||
throw Constructing_test_failed();
|
throw Constructing_test_failed();
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t const total_bytes = _info.block_count * _info.block_size;
|
size_t const total_bytes = _info.block_count * _info.block_size;
|
||||||
if (_length > total_bytes - (_start * _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();
|
throw Constructing_test_failed();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_info.block_size > _size || (_size % _info.block_size) != 0) {
|
if (_info.block_size > _size || (_size % _info.block_size) != 0) {
|
||||||
Genode::error("request size invalid");
|
error("request size invalid");
|
||||||
throw Constructing_test_failed();
|
throw Constructing_test_failed();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_node.attribute_value("write", false)) {
|
|
||||||
_op = Block::Packet_descriptor::WRITE;
|
|
||||||
}
|
|
||||||
|
|
||||||
_size_in_blocks = _size / _info.block_size;
|
_size_in_blocks = _size / _info.block_size;
|
||||||
_length_in_blocks = _length / _info.block_size;
|
_length_in_blocks = _length / _info.block_size;
|
||||||
_end = _start + _length_in_blocks;
|
_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,
|
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"; }
|
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_ */
|
#endif /* _TEST_PING_PONG_H_ */
|
||||||
|
|
|
@ -14,9 +14,8 @@
|
||||||
#ifndef _TEST_RANDOM_H_
|
#ifndef _TEST_RANDOM_H_
|
||||||
#define _TEST_RANDOM_H_
|
#define _TEST_RANDOM_H_
|
||||||
|
|
||||||
namespace Test {
|
namespace Test { struct Random; }
|
||||||
struct Random;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace Util {
|
namespace Util {
|
||||||
|
|
||||||
|
@ -79,236 +78,101 @@ namespace Util {
|
||||||
*/
|
*/
|
||||||
struct Test::Random : Test_base
|
struct Test::Random : Test_base
|
||||||
{
|
{
|
||||||
Genode::Env &_env;
|
|
||||||
Genode::Allocator &_alloc;
|
|
||||||
|
|
||||||
Block::Packet_descriptor::Opcode _op {
|
|
||||||
Block::Packet_descriptor::READ };
|
|
||||||
bool _alternate_access { false };
|
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;
|
Util::Xoroshiro _random;
|
||||||
|
|
||||||
size_t _size { 0 };
|
size_t const _size = _node.attribute_value("size", Number_of_bytes());
|
||||||
uint64_t _length { 0 };
|
uint64_t const _length = _node.attribute_value("length", Number_of_bytes());
|
||||||
char _scratch_buffer[1u<<20] { };
|
|
||||||
|
|
||||||
size_t _blocks { 0 };
|
Block::Operation::Type _op_type = Block::Operation::Type::READ;
|
||||||
|
|
||||||
/* _synchronous controls bulk */
|
block_number_t _next_block()
|
||||||
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()
|
|
||||||
{
|
{
|
||||||
uint64_t r = 0;
|
uint64_t r = 0;
|
||||||
|
block_number_t max = _info.block_count;
|
||||||
|
if (max >= _size_in_blocks + 1)
|
||||||
|
max -= _size_in_blocks + 1;
|
||||||
do {
|
do {
|
||||||
r = _random.get() % _info.block_count;
|
r = _random.get() % max;
|
||||||
} while (r + _size_in_blocks > _info.block_count);
|
} while (r + _size_in_blocks > _info.block_count);
|
||||||
|
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
void _handle_submit()
|
template <typename... ARGS>
|
||||||
{
|
Random(ARGS &&...args)
|
||||||
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)
|
|
||||||
:
|
:
|
||||||
Test_base(finished_sig), _env(env), _alloc(alloc),
|
Test_base(args...),
|
||||||
_random(node.attribute_value("seed", 42u)),
|
_random(_node.attribute_value("seed", 42UL))
|
||||||
_node(node)
|
{ }
|
||||||
|
|
||||||
|
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)) {
|
if (_size > sizeof(_scratch_buffer)) {
|
||||||
Genode::error("request size exceeds scratch buffer size");
|
error("request size exceeds scratch buffer size");
|
||||||
throw Constructing_test_failed();
|
throw Constructing_test_failed();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!_size || !_length) {
|
if (!_size || !_length) {
|
||||||
Genode::error("request size or length invalid");
|
error("request size or length invalid");
|
||||||
throw Constructing_test_failed();
|
throw Constructing_test_failed();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_info.block_size > _size || (_size % _info.block_size) != 0) {
|
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();
|
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);
|
bool const w = _node.attribute_value("write", false);
|
||||||
if (r) { _op = Block::Packet_descriptor::WRITE; }
|
if (w) { _op_type = Block::Operation::Type::WRITE; }
|
||||||
|
|
||||||
bool w = _node.attribute_value("read", false);
|
|
||||||
if (w) { _op = Block::Packet_descriptor::WRITE; }
|
|
||||||
|
|
||||||
_alternate_access = w && r;
|
_alternate_access = w && r;
|
||||||
|
|
||||||
_size_in_blocks = _size / _info.block_size;
|
_size_in_blocks = _size / _info.block_size;
|
||||||
_length_in_blocks = _length / _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();
|
if (_bytes >= _length)
|
||||||
_block.destruct();
|
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,
|
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"; }
|
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_ */
|
#endif /* _TEST_RANDOM_H_ */
|
||||||
|
|
|
@ -14,194 +14,68 @@
|
||||||
#ifndef _TEST_REPLAY_H_
|
#ifndef _TEST_REPLAY_H_
|
||||||
#define _TEST_REPLAY_H_
|
#define _TEST_REPLAY_H_
|
||||||
|
|
||||||
namespace Test {
|
namespace Test { struct Replay; }
|
||||||
struct Replay;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Replay test
|
* Replay test
|
||||||
*
|
*
|
||||||
* This test replays a recorded sequence of Block session
|
* This test replays a recorded sequence of Block session requests.
|
||||||
* requests.
|
|
||||||
*/
|
*/
|
||||||
struct Test::Replay : Test_base
|
struct Test::Replay : Test_base
|
||||||
{
|
{
|
||||||
Genode::Env &env;
|
using Test_base::Test_base;
|
||||||
Genode::Allocator &alloc;
|
|
||||||
|
|
||||||
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 {
|
try {
|
||||||
while (_block->tx()->ready_to_submit() && more) {
|
_node.for_each_sub_node("request", [&](Xml_node request) {
|
||||||
/* 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);
|
|
||||||
|
|
||||||
bool const write = req.op == Block::Packet_descriptor::WRITE;
|
auto op_type = [&] ()
|
||||||
|
{
|
||||||
|
typedef String<8> Type;
|
||||||
|
|
||||||
/* simulate write */
|
if (request.attribute_value("type", Type()) == "read")
|
||||||
if (write) {
|
return Block::Operation::Type::READ;
|
||||||
char * const content = _block->tx()->packet_content(p);
|
|
||||||
Genode::memcpy(content, _scratch_buffer, p.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
_block->tx()->submit_packet(p);
|
if (request.attribute_value("type", Type()) == "write")
|
||||||
|
return Block::Operation::Type::WRITE;
|
||||||
|
|
||||||
Genode::destroy(&alloc, &req);
|
error("operation type not defined: ", request);
|
||||||
more = _bulk;
|
throw 1;
|
||||||
});
|
};
|
||||||
}
|
|
||||||
} catch (...) { }
|
|
||||||
}
|
|
||||||
|
|
||||||
void _finish()
|
Block::Operation const operation
|
||||||
{
|
{
|
||||||
_end_time = _timer->elapsed_ms();
|
.type = op_type(),
|
||||||
|
.block_number = request.attribute_value("lba", (block_number_t)0),
|
||||||
|
.count = request.attribute_value("count", 0UL)
|
||||||
|
};
|
||||||
|
|
||||||
Test_base::_finish();
|
_job_cnt++;
|
||||||
}
|
new (&_alloc) Job(*_block, operation, _job_cnt);
|
||||||
|
|
||||||
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;
|
|
||||||
});
|
});
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
Genode::error("could not read request list");
|
error("could not read request list");
|
||||||
|
|
||||||
|
_block->dissolve_all_jobs([&] (Job &job) { destroy(_alloc, &job); });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/********************
|
void _spawn_job() override { }
|
||||||
** Test interface **
|
|
||||||
********************/
|
|
||||||
|
|
||||||
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,
|
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"; }
|
char const *name() const override { return "replay"; }
|
||||||
|
|
||||||
|
void print(Output &out) const override
|
||||||
|
{
|
||||||
|
Genode::print(out, name());
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#endif /* _TEST_REPLAY_H_ */
|
#endif /* _TEST_REPLAY_H_ */
|
||||||
|
|
|
@ -14,9 +14,7 @@
|
||||||
#ifndef _TEST_SEQUENTIAL_H_
|
#ifndef _TEST_SEQUENTIAL_H_
|
||||||
#define _TEST_SEQUENTIAL_H_
|
#define _TEST_SEQUENTIAL_H_
|
||||||
|
|
||||||
namespace Test {
|
namespace Test { struct Sequential; }
|
||||||
struct Sequential;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -27,218 +25,65 @@ namespace Test {
|
||||||
*/
|
*/
|
||||||
struct Test::Sequential : Test_base
|
struct Test::Sequential : Test_base
|
||||||
{
|
{
|
||||||
Genode::Env &_env;
|
block_number_t _start = _node.attribute_value("start", 0u);
|
||||||
Genode::Allocator &_alloc;
|
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::Operation::Type const _op_type = _node.attribute_value("write", false)
|
||||||
Block::sector_t _start { 0 };
|
? Block::Operation::Type::WRITE
|
||||||
size_t _length { 0 };
|
: Block::Operation::Type::READ;
|
||||||
size_t _size { 0 };
|
|
||||||
|
|
||||||
/* _synchronous controls bulk */
|
using Test_base::Test_base;
|
||||||
bool _synchronous { false };
|
|
||||||
|
|
||||||
Block::Packet_descriptor::Opcode _op {
|
void _init() override
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
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)) {
|
if (_size > sizeof(_scratch_buffer)) {
|
||||||
Genode::error("request size exceeds scratch buffer size");
|
error("request size exceeds scratch buffer size");
|
||||||
throw Constructing_test_failed();
|
throw Constructing_test_failed();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_info.block_size > _size || (_size % _info.block_size) != 0) {
|
if (_info.block_size > _size || (_size % _info.block_size) != 0) {
|
||||||
Genode::error("request size invalid");
|
error("request size invalid");
|
||||||
throw Constructing_test_failed();
|
throw Constructing_test_failed();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_node.attribute_value("write", false)) {
|
|
||||||
_op = Block::Packet_descriptor::WRITE;
|
|
||||||
}
|
|
||||||
|
|
||||||
_size_in_blocks = _size / _info.block_size;
|
_size_in_blocks = _size / _info.block_size;
|
||||||
_length_in_blocks = _length / _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();
|
if (_bytes >= _length)
|
||||||
_block.destruct();
|
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,
|
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"; }
|
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_ */
|
#endif /* _TEST_SEQUENTIAL_H_ */
|
||||||
|
|
Loading…
Reference in New Issue