/* * \brief Block session testing - random test * \author Josef Soentgen * \date 2016-07-04 */ /* * Copyright (C) 2016-2018 Genode Labs GmbH * * This file is part of the Genode OS framework, which is distributed * under the terms of the GNU General Public License version 2. */ #ifndef _TEST_RANDOM_H_ #define _TEST_RANDOM_H_ namespace Test { struct Random; } namespace Util { using namespace Genode; /* * Xoroshiro128+ written in 2014-2016 by Sebastiano Vigna (vigna@acm.org) * * (see http://xoroshiro.di.unimi.it/xorshift128plus.c and * http://xoroshiro.di.unimi.it/splitmix64.c) */ struct Xoroshiro { uint64_t seed; uint64_t splitmix64() { uint64_t z = (seed += __UINT64_C(0x9E3779B97F4A7C15)); z = (z ^ (z >> 30)) * __UINT64_C(0xBF58476D1CE4E5B9); z = (z ^ (z >> 27)) * __UINT64_C(0x94D049BB133111EB); return z ^ (z >> 31); } Xoroshiro(uint64_t seed) : seed(seed) { s[0] = splitmix64(); s[1] = splitmix64(); } uint64_t s[2]; static uint64_t rotl(uint64_t const x, int k) { return (x << k) | (x >> (64 - k)); } uint64_t get() { uint64_t const s0 = s[0]; uint64_t s1 = s[1]; uint64_t const result = s0 + s1; s1 ^= s0; s[0] = rotl(s0, 55) ^ s1 ^ (s1 << 14); s[1] = rotl(s1, 36); return result; } }; } /* * Random test * * This test reads or writes the given number of bytes in a * deterministic order that depends and the seed value of a * PRNG in particular sized requests. */ struct Test::Random : Test_base { Genode::Env &_env; Genode::Allocator &_alloc; Block::Packet_descriptor::Opcode _op { Block::Packet_descriptor::READ }; bool _alternate_access { false }; /* block session infos */ enum { TX_BUF_SIZE = 4 * 1024 * 1024, }; Genode::Allocator_avl _block_alloc { &_alloc }; Genode::Constructible _block { }; Genode::Constructible _timer { }; Util::Xoroshiro _random; size_t _size { 0 }; uint64_t _length { 0 }; char _scratch_buffer[1u<<20] { }; size_t _blocks { 0 }; /* _synchronous controls bulk */ bool _synchronous { false }; Genode::Constructible> _progress_timeout { }; void _handle_progress_timeout(Genode::Duration) { Genode::log("progress: rx:", _rx, " tx:", _tx); } Block::sector_t _next_block() { uint64_t r = 0; do { r = _random.get() % _info.block_count; } while (r + _size_in_blocks > _info.block_count); return r; } void _handle_submit() { try { bool next = true; while (_blocks < _length_in_blocks && _block->tx()->ready_to_submit() && next) { Block::Packet_descriptor tmp = _block->alloc_packet(_size); Block::sector_t lba = _next_block(); Block::Packet_descriptor::Opcode op = _alternate_access ? (lba & 0x1) ? Block::Packet_descriptor::WRITE : Block::Packet_descriptor::READ : _op; Block::Packet_descriptor p(tmp, op, lba, _size_in_blocks); bool const write = op == Block::Packet_descriptor::WRITE; /* simulate write */ if (write) { char * const content = _block->tx()->packet_content(p); Genode::memcpy(content, _scratch_buffer, p.size()); } if (_verbose) { Genode::log("submit: lba:", lba, " size:", _size, " ", write ? "tx" : "rx"); } _block->tx()->submit_packet(p); _blocks += _size_in_blocks; next = !_synchronous; } } catch (...) { } } void _handle_ack() { if (_finished) { return; } while (_block->tx()->ack_avail()) { Block::Packet_descriptor p = _block->tx()->get_acked_packet(); if (!p.succeeded()) { Genode::error("processing ", p.block_number(), " ", p.block_count(), " failed"); if (_stop_on_error) { throw Test_failed(); } else { _finish(); break; } } size_t const psize = p.size(); size_t const count = psize / _info.block_size; Block::Packet_descriptor::Opcode const op = p.operation(); bool const read = op == Block::Packet_descriptor::READ; /* simulate read */ if (read) { char * const content = _block->tx()->packet_content(p); Genode::memcpy(_scratch_buffer, content, p.size()); } if (_verbose) { Genode::log("ack: lba:", p.block_number(), " size:", p.size(), " ", read ? "rx" : "tx"); } _rx += (op == Block::Packet_descriptor::READ) * count; _tx += (op == Block::Packet_descriptor::WRITE) * count; _bytes += psize; _block->tx()->release_packet(p); } if (_bytes >= _length) { _success = true; _finish(); return; } _handle_submit(); } void _finish() { _end_time = _timer->elapsed_ms(); Test_base::_finish(); } Genode::Signal_handler _ack_sigh { _env.ep(), *this, &Random::_handle_ack }; Genode::Signal_handler _submit_sigh { _env.ep(), *this, &Random::_handle_submit }; Genode::Xml_node _node; /** * Constructor * * \param block Block session reference * \param node node containing the test configuration */ Random(Genode::Env &env, Genode::Allocator &alloc, Genode::Xml_node node, Genode::Signal_context_capability finished_sig) : Test_base(finished_sig), _env(env), _alloc(alloc), _random(node.attribute_value("seed", 42u)), _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(); _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"); throw Constructing_test_failed(); } if (!_size || !_length) { Genode::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); throw Constructing_test_failed(); } _synchronous = _node.attribute_value("synchronous", false); 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; } _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 { _timer.destruct(); _block.destruct(); return Result(_success, _end_time - _start_time, _bytes, _rx, _tx, _size, _info.block_size); } char const *name() const override { return "random"; } }; #endif /* _TEST_RANDOM_H_ */