mirror of
https://github.com/genodelabs/genode.git
synced 2024-12-21 14:37:50 +00:00
block: extend block session test framework
This commit is contained in:
parent
e73a71be12
commit
35bb156972
@ -12,7 +12,7 @@ set build_components {
|
||||
core init
|
||||
drivers/timer
|
||||
drivers/usb
|
||||
test/block
|
||||
test/blk/cli
|
||||
}
|
||||
|
||||
lappend_if [have_spec acpi] build_components drivers/acpi
|
||||
@ -93,7 +93,7 @@ append config {
|
||||
</start>
|
||||
<start name="test-usb">
|
||||
<resource name="RAM" quantum="2M" />
|
||||
<binary name="test-block" />
|
||||
<binary name="test-blk-cli" />
|
||||
</start>
|
||||
</config>
|
||||
}
|
||||
@ -105,7 +105,7 @@ install_config $config
|
||||
|
||||
# generic modules
|
||||
set boot_modules {
|
||||
core init timer usb_drv test-block
|
||||
core init timer usb_drv test-blk-cli
|
||||
}
|
||||
|
||||
lappend_if [have_spec acpi] boot_modules acpi_drv
|
||||
@ -142,7 +142,7 @@ append qemu_args { \
|
||||
-device usb-storage,bus=ehci.0,drive=disk \
|
||||
-boot order=d }
|
||||
|
||||
run_genode_until {.*child exited with exit value 0.*} 40
|
||||
run_genode_until {.*child exited with exit value 0.*} 100
|
||||
|
||||
puts "\ntest succeeded\n"
|
||||
|
||||
|
@ -7,7 +7,7 @@ if {![have_spec x86_32] && ![have_spec exynos5]} {
|
||||
# Build
|
||||
#
|
||||
|
||||
set build_components { core init drivers/timer drivers/ahci test/block }
|
||||
set build_components { core init drivers/timer drivers/ahci test/blk/cli }
|
||||
|
||||
lappend_if [have_spec x86_32] build_components drivers/pci
|
||||
lappend_if [have_spec acpi] build_components drivers/acpi
|
||||
@ -93,7 +93,7 @@ append config {
|
||||
<any-service> <parent /> <any-child /></any-service>
|
||||
</route>
|
||||
</start>
|
||||
<start name="test-block">
|
||||
<start name="test-blk-cli">
|
||||
<resource name="RAM" quantum="2M" />
|
||||
<route>
|
||||
<service name="Block"><child name="ahci"/></service>
|
||||
@ -108,7 +108,7 @@ install_config $config
|
||||
# Boot modules
|
||||
#
|
||||
|
||||
set boot_modules { core init timer ahci test-block }
|
||||
set boot_modules { core init timer ahci test-blk-cli }
|
||||
|
||||
append_if [have_spec x86_32] boot_modules { pci_drv acpi_drv }
|
||||
append_if [have_spec nova] boot_modules pci_device_pd
|
||||
@ -137,43 +137,4 @@ if { [file exists $disk_image] == 0 } then {
|
||||
# Test
|
||||
#
|
||||
|
||||
run_genode_until "child exited with exit value 0.*\n" 10
|
||||
|
||||
# pay only attention to the output of test-block
|
||||
grep_output {^\[init -> test-block.*Comparing}
|
||||
|
||||
compare_output_to {
|
||||
[init -> test-block] Comparing block 0000000000: success
|
||||
[init -> test-block] Comparing block 0000000640: success
|
||||
[init -> test-block] Comparing block 0000001280: success
|
||||
[init -> test-block] Comparing block 0000001920: success
|
||||
[init -> test-block] Comparing block 0000002560: success
|
||||
[init -> test-block] Comparing block 0000003200: success
|
||||
[init -> test-block] Comparing block 0000003840: success
|
||||
[init -> test-block] Comparing block 0000004480: success
|
||||
[init -> test-block] Comparing block 0000005120: success
|
||||
[init -> test-block] Comparing block 0000005760: success
|
||||
[init -> test-block] Comparing block 0000006400: success
|
||||
[init -> test-block] Comparing block 0000007040: success
|
||||
[init -> test-block] Comparing block 0000007680: success
|
||||
[init -> test-block] Comparing block 0000008320: success
|
||||
[init -> test-block] Comparing block 0000008960: success
|
||||
[init -> test-block] Comparing block 0000009600: success
|
||||
[init -> test-block] Comparing block 0000010240: success
|
||||
[init -> test-block] Comparing block 0000010880: success
|
||||
[init -> test-block] Comparing block 0000011520: success
|
||||
[init -> test-block] Comparing block 0000012160: success
|
||||
[init -> test-block] Comparing block 0000012800: success
|
||||
[init -> test-block] Comparing block 0000013440: success
|
||||
[init -> test-block] Comparing block 0000014080: success
|
||||
[init -> test-block] Comparing block 0000014720: success
|
||||
[init -> test-block] Comparing block 0000015360: success
|
||||
[init -> test-block] Comparing block 0000016000: success
|
||||
[init -> test-block] Comparing block 0000016640: success
|
||||
[init -> test-block] Comparing block 0000017280: success
|
||||
[init -> test-block] Comparing block 0000017920: success
|
||||
[init -> test-block] Comparing block 0000018560: success
|
||||
[init -> test-block] Comparing block 0000019200: success
|
||||
[init -> test-block] Comparing block 0000019840: success
|
||||
}
|
||||
|
||||
run_genode_until "Tests finished successfully.*\n" 50
|
||||
|
54
os/run/blk.run
Normal file
54
os/run/blk.run
Normal file
@ -0,0 +1,54 @@
|
||||
#
|
||||
# \brief Test of Block session interface
|
||||
# \author Stefan Kalkowski
|
||||
# \date 2013-12-10
|
||||
#
|
||||
|
||||
#
|
||||
# Build
|
||||
#
|
||||
build { core init drivers/timer test/blk lib/trace/policy/rpc_name }
|
||||
create_boot_directory
|
||||
|
||||
#
|
||||
# Generate config
|
||||
#
|
||||
install_config {
|
||||
<config>
|
||||
<parent-provides>
|
||||
<service name="ROM"/>
|
||||
<service name="RAM"/>
|
||||
<service name="IRQ"/>
|
||||
<service name="IO_MEM"/>
|
||||
<service name="IO_PORT"/>
|
||||
<service name="CAP"/>
|
||||
<service name="PD"/>
|
||||
<service name="RM"/>
|
||||
<service name="CPU"/>
|
||||
<service name="LOG"/>
|
||||
<service name="SIGNAL"/>
|
||||
<service name="TRACE"/>
|
||||
</parent-provides>
|
||||
<default-route>
|
||||
<any-service> <parent/> <any-child/> </any-service>
|
||||
</default-route>
|
||||
<start name="timer">
|
||||
<resource name="RAM" quantum="1M"/>
|
||||
<provides><service name="Timer"/></provides>
|
||||
</start>
|
||||
<start name="test-blk-srv">
|
||||
<resource name="RAM" quantum="10M" />
|
||||
<provides><service name="Block" /></provides>
|
||||
</start>
|
||||
<start name="test-blk-cli">
|
||||
<resource name="RAM" quantum="50M" />
|
||||
</start>
|
||||
</config> }
|
||||
|
||||
#
|
||||
# Boot modules
|
||||
#
|
||||
build_boot_image { core init timer test-blk-srv test-blk-cli rpc_name }
|
||||
|
||||
append qemu_args " -nographic -m 64 "
|
||||
run_genode_until "Tests finished successfully.*\n" 100
|
@ -7,7 +7,7 @@ set build_components {
|
||||
core init
|
||||
drivers/timer
|
||||
drivers/sd_card
|
||||
test/block
|
||||
test/blk/cli
|
||||
}
|
||||
lappend_if [have_spec platform_arndale] build_components drivers/platform
|
||||
|
||||
@ -52,7 +52,7 @@ append config {
|
||||
<resource name="RAM" quantum="1M"/>
|
||||
<provides><service name="Block"/></provides>
|
||||
</start>
|
||||
<start name="test-block">
|
||||
<start name="test-blk-cli">
|
||||
<resource name="RAM" quantum="1M" />
|
||||
</start>
|
||||
</config> }
|
||||
@ -69,7 +69,7 @@ set boot_modules {
|
||||
core init
|
||||
timer
|
||||
sd_card_drv
|
||||
test-block
|
||||
test-blk-cli
|
||||
}
|
||||
lappend_if [have_spec platform_arndale] boot_modules platform_drv
|
||||
|
||||
|
399
os/src/test/blk/cli/main.cc
Normal file
399
os/src/test/blk/cli/main.cc
Normal file
@ -0,0 +1,399 @@
|
||||
/**
|
||||
* \brief Block session tests - client side.
|
||||
* \author Stefan Kalkowski
|
||||
* \date 2013-12-10
|
||||
*/
|
||||
|
||||
#include <base/allocator_avl.h>
|
||||
#include <block_session/connection.h>
|
||||
#include <timer_session/connection.h>
|
||||
#include <os/ring_buffer.h>
|
||||
|
||||
static Genode::size_t blk_sz; /* block size of the device */
|
||||
static Block::sector_t blk_cnt; /* number of blocks of device */
|
||||
static Block::Session::Operations blk_ops; /* supported operations */
|
||||
|
||||
|
||||
/**
|
||||
* Virtual base class of all test scenarios, provides basic signal handling
|
||||
*/
|
||||
class Test
|
||||
{
|
||||
public:
|
||||
|
||||
struct Exception : Genode::Exception
|
||||
{
|
||||
virtual void print_error() = 0;
|
||||
};
|
||||
|
||||
class Block_exception : public Exception
|
||||
{
|
||||
protected:
|
||||
|
||||
Block::sector_t _nr;
|
||||
Genode::size_t _cnt;
|
||||
bool _write;
|
||||
|
||||
public:
|
||||
|
||||
Block_exception(Block::sector_t nr, Genode::size_t cnt,
|
||||
bool write)
|
||||
: _nr(nr), _cnt(cnt), _write(write) {}
|
||||
|
||||
virtual void print_error() {
|
||||
PINF("couldn't %s block %lld - %lld",
|
||||
_write ? "write" : "read", _nr, _nr+_cnt); }
|
||||
};
|
||||
|
||||
struct Submit_queue_full : Exception {
|
||||
void print_error() { PINF("The submit queue is full!"); } };
|
||||
|
||||
struct Timeout : Exception {
|
||||
void print_error() { PINF("Test timed out!"); } };
|
||||
|
||||
virtual void perform() = 0;
|
||||
virtual void ack_avail() = 0;
|
||||
|
||||
protected:
|
||||
|
||||
Genode::Allocator_avl _alloc;
|
||||
Block::Connection _session;
|
||||
Genode::Signal_receiver _receiver;
|
||||
Genode::Signal_dispatcher<Test> _disp_ack;
|
||||
Genode::Signal_dispatcher<Test> _disp_submit;
|
||||
Genode::Signal_dispatcher<Test> _disp_timeout;
|
||||
Timer::Connection _timer;
|
||||
bool _handle;
|
||||
|
||||
Genode::size_t _shared_buffer_size(Genode::size_t bulk)
|
||||
{
|
||||
return bulk +
|
||||
sizeof(Block::Session::Tx_policy::Ack_queue) +
|
||||
sizeof(Block::Session::Tx_policy::Submit_queue);
|
||||
}
|
||||
|
||||
void _ack_avail(unsigned) { ack_avail(); }
|
||||
void _ready_to_submit(unsigned) { _handle = false; }
|
||||
void _timeout(unsigned) { throw Timeout(); }
|
||||
|
||||
Test(Genode::size_t bulk_buffer_size,
|
||||
unsigned timeout_ms)
|
||||
: _alloc(Genode::env()->heap()),
|
||||
_session(&_alloc, _shared_buffer_size(bulk_buffer_size)),
|
||||
_disp_ack(_receiver, *this, &Test::_ack_avail),
|
||||
_disp_submit(_receiver, *this, &Test::_ready_to_submit),
|
||||
_disp_timeout(_receiver, *this, &Test::_timeout)
|
||||
{
|
||||
_session.tx_channel()->sigh_ack_avail(_disp_ack);
|
||||
_session.tx_channel()->sigh_ready_to_submit(_disp_submit);
|
||||
|
||||
if (timeout_ms) {
|
||||
_timer.sigh(_disp_timeout);
|
||||
_timer.trigger_once(1000*timeout_ms);
|
||||
}
|
||||
}
|
||||
|
||||
void _handle_signal()
|
||||
{
|
||||
_handle = true;
|
||||
|
||||
while (_handle) {
|
||||
Genode::Signal s = _receiver.wait_for_signal();
|
||||
static_cast<Genode::Signal_dispatcher_base *>
|
||||
(s.context())->dispatch(s.num());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template <unsigned BULK_BLK_NR, unsigned NR_PER_REQ>
|
||||
struct Read_test : Test
|
||||
{
|
||||
bool done;
|
||||
|
||||
Read_test(unsigned timeo_ms)
|
||||
: Test(BULK_BLK_NR*blk_sz, timeo_ms), done(false) { }
|
||||
|
||||
void perform()
|
||||
{
|
||||
PINF("reading block 0 - %llu, %u per request",
|
||||
blk_cnt - 1, NR_PER_REQ);
|
||||
|
||||
for (Block::sector_t nr = 0, cnt = NR_PER_REQ; nr < blk_cnt;
|
||||
nr += cnt) {
|
||||
|
||||
while (!_session.tx()->ready_to_submit())
|
||||
_handle_signal();
|
||||
|
||||
cnt = Genode::min<Block::sector_t>(NR_PER_REQ, blk_cnt-nr);
|
||||
|
||||
try {
|
||||
Block::Packet_descriptor p(
|
||||
_session.dma_alloc_packet(cnt*blk_sz),
|
||||
Block::Packet_descriptor::READ, nr, cnt);
|
||||
_session.tx()->submit_packet(p);
|
||||
} catch(Block::Session::Tx::Source::Packet_alloc_failed) {
|
||||
cnt = 0; /* retry the current block number */
|
||||
_handle_signal();
|
||||
}
|
||||
}
|
||||
|
||||
while (!done)
|
||||
_handle_signal();
|
||||
}
|
||||
|
||||
void ack_avail()
|
||||
{
|
||||
_handle = false;
|
||||
|
||||
while (_session.tx()->ack_avail()) {
|
||||
Block::Packet_descriptor p = _session.tx()->get_acked_packet();
|
||||
|
||||
if (!p.succeeded())
|
||||
throw Block_exception(p.block_number(), p.block_count(),
|
||||
false);
|
||||
|
||||
if ((p.block_number() + p.block_count()) == blk_cnt)
|
||||
done = true;
|
||||
|
||||
_session.tx()->release_packet(p);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template <unsigned BULK_BLK_NR, unsigned NR_PER_REQ, unsigned BATCH>
|
||||
struct Write_test : Test
|
||||
{
|
||||
struct Invalid_dimensions : Exception {
|
||||
void print_error() { PINF("Invalid bulk buffer, or batch size!"); } };
|
||||
|
||||
struct Integrity_exception : Block_exception
|
||||
{
|
||||
Integrity_exception(Block::sector_t nr, Genode::size_t cnt)
|
||||
: Block_exception(nr, cnt, false) {}
|
||||
|
||||
void print_error() {
|
||||
PINF("Integrity check failed: block %lld - %lld", _nr, _nr+_cnt); }
|
||||
};
|
||||
|
||||
typedef Ring_buffer<Block::Packet_descriptor, BATCH+1,
|
||||
Ring_buffer_unsynchronized> Req_buffer;
|
||||
|
||||
Req_buffer read_packets;
|
||||
Req_buffer write_packets;
|
||||
|
||||
Write_test(unsigned timeo_ms)
|
||||
: Test(BULK_BLK_NR*blk_sz, timeo_ms)
|
||||
{
|
||||
if (BULK_BLK_NR < BATCH*NR_PER_REQ ||
|
||||
BATCH > Block::Session::TX_QUEUE_SIZE ||
|
||||
BULK_BLK_NR % BATCH != 0)
|
||||
throw Invalid_dimensions();
|
||||
}
|
||||
|
||||
bool compare(Block::Packet_descriptor &r, Block::Packet_descriptor &w)
|
||||
{
|
||||
signed char *dst = (signed char*)_session.tx()->packet_content(w),
|
||||
*src = (signed char*)_session.tx()->packet_content(r);
|
||||
for (Genode::size_t i = 0; i < blk_sz; i++)
|
||||
if (dst[i] != src[i])
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void compare()
|
||||
{
|
||||
while (!read_packets.empty()) {
|
||||
Block::Packet_descriptor r = read_packets.get();
|
||||
while (true) {
|
||||
Block::Packet_descriptor w = write_packets.get();
|
||||
if (r.block_number() == w.block_number()) {
|
||||
if (!compare(r,w))
|
||||
throw Integrity_exception(r.block_number(),
|
||||
r.block_count());
|
||||
break;
|
||||
}
|
||||
write_packets.add(w);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void write(signed char val)
|
||||
{
|
||||
while (!read_packets.empty()) {
|
||||
Block::Packet_descriptor r = read_packets.get();
|
||||
Block::Packet_descriptor w(_session.dma_alloc_packet(r.block_count()
|
||||
*blk_sz),
|
||||
Block::Packet_descriptor::WRITE,
|
||||
r.block_number(), r.block_count());
|
||||
signed char *dst = (signed char*)_session.tx()->packet_content(w),
|
||||
*src = (signed char*)_session.tx()->packet_content(r);
|
||||
for (Genode::size_t i = 0; i < blk_sz; i++)
|
||||
dst[i] = src[i] + val;
|
||||
_session.tx()->submit_packet(w);
|
||||
}
|
||||
while (write_packets.avail_capacity())
|
||||
_handle_signal();
|
||||
}
|
||||
|
||||
void read(Block::sector_t start, Block::sector_t end)
|
||||
{
|
||||
using namespace Block;
|
||||
|
||||
for (sector_t nr = start, cnt = NR_PER_REQ; nr < end;
|
||||
cnt = Genode::min<sector_t>(NR_PER_REQ, end-nr),
|
||||
nr += cnt) {
|
||||
Block::Packet_descriptor p(_session.dma_alloc_packet(cnt*blk_sz),
|
||||
Block::Packet_descriptor::READ, nr, cnt);
|
||||
_session.tx()->submit_packet(p);
|
||||
}
|
||||
while (read_packets.avail_capacity())
|
||||
_handle_signal();
|
||||
}
|
||||
|
||||
void batch(Block::sector_t start, Block::sector_t end, signed char val)
|
||||
{
|
||||
read(start, end);
|
||||
write(val);
|
||||
read(start, end);
|
||||
compare();
|
||||
}
|
||||
|
||||
void perform()
|
||||
{
|
||||
if (!blk_ops.supported(Block::Packet_descriptor::WRITE))
|
||||
return;
|
||||
|
||||
PINF("read/write/compare block 0 - %llu, %u per request",
|
||||
blk_cnt - 1, NR_PER_REQ);
|
||||
|
||||
for (Block::sector_t nr = 0, cnt = BATCH*NR_PER_REQ; nr < blk_cnt;
|
||||
cnt = Genode::min<Block::sector_t>(BATCH*NR_PER_REQ, blk_cnt-nr),
|
||||
nr += cnt) {
|
||||
batch(nr, nr + cnt, 1);
|
||||
batch(nr, nr + cnt, -1);
|
||||
}
|
||||
}
|
||||
|
||||
void ack_avail()
|
||||
{
|
||||
_handle = false;
|
||||
|
||||
while (_session.tx()->ack_avail()) {
|
||||
Block::Packet_descriptor p = _session.tx()->get_acked_packet();
|
||||
bool write = p.operation() == Block::Packet_descriptor::WRITE;
|
||||
if (!p.succeeded())
|
||||
throw Block_exception(p.block_number(), p.block_count(), write);
|
||||
if (write)
|
||||
write_packets.add(p);
|
||||
else
|
||||
read_packets.add(p);
|
||||
_session.tx()->release_packet(p);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct Violation_test : Test
|
||||
{
|
||||
struct Write_on_read_only : Exception {
|
||||
void print_error() { PINF("write on read-only device succeeded!"); } };
|
||||
|
||||
struct Range_check_failed : Block_exception
|
||||
{
|
||||
Range_check_failed(Block::sector_t nr, Genode::size_t cnt)
|
||||
: Block_exception(nr, cnt, false) {}
|
||||
|
||||
void print_error() {
|
||||
PINF("Range check failed: access to block %lld - %lld succeeded",
|
||||
_nr, _nr+_cnt); }
|
||||
};
|
||||
|
||||
int p_in_fly;
|
||||
|
||||
Violation_test(unsigned timeo) : Test(20*blk_sz, timeo), p_in_fly(0) {}
|
||||
|
||||
void req(Block::sector_t nr, Genode::size_t cnt, bool write)
|
||||
{
|
||||
Block::Packet_descriptor p(_session.dma_alloc_packet(blk_sz),
|
||||
write ? Block::Packet_descriptor::WRITE
|
||||
: Block::Packet_descriptor::READ,
|
||||
nr, cnt);
|
||||
_session.tx()->submit_packet(p);
|
||||
p_in_fly++;
|
||||
}
|
||||
|
||||
void perform()
|
||||
{
|
||||
if (!blk_ops.supported(Block::Packet_descriptor::WRITE))
|
||||
req(0, 1, true);
|
||||
|
||||
req(blk_cnt, 1, false);
|
||||
req(blk_cnt-1, 2, false);
|
||||
|
||||
while (p_in_fly > 0)
|
||||
_handle_signal();
|
||||
}
|
||||
|
||||
void ack_avail()
|
||||
{
|
||||
_handle = false;
|
||||
|
||||
while (_session.tx()->ack_avail()) {
|
||||
Block::Packet_descriptor p = _session.tx()->get_acked_packet();
|
||||
if (p.succeeded()) {
|
||||
if (p.operation() == Block::Packet_descriptor::WRITE)
|
||||
throw Write_on_read_only();
|
||||
throw Range_check_failed(p.block_number(), p.block_count());
|
||||
}
|
||||
_session.tx()->release_packet(p);
|
||||
p_in_fly--;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template <typename TEST>
|
||||
void perform(unsigned timeo_ms = 0)
|
||||
{
|
||||
TEST t(timeo_ms);
|
||||
t.perform();
|
||||
}
|
||||
|
||||
|
||||
int main()
|
||||
{
|
||||
try {
|
||||
/**
|
||||
* First we ask for the block size of the driver to dimension
|
||||
* the queue size for our tests. Moreover, we implicitely test,
|
||||
* whether closing and opening again works for the driver
|
||||
*/
|
||||
{
|
||||
Genode::Allocator_avl alloc(Genode::env()->heap());
|
||||
Block::Connection blk(&alloc);
|
||||
blk.info(&blk_cnt, &blk_sz, &blk_ops);
|
||||
}
|
||||
|
||||
PINF("block device with block size %zd sector count %lld",
|
||||
blk_sz, blk_cnt);
|
||||
|
||||
perform<Read_test<Block::Session::TX_QUEUE_SIZE-10,
|
||||
Block::Session::TX_QUEUE_SIZE-10> >();
|
||||
perform<Read_test<Block::Session::TX_QUEUE_SIZE*5, 1> >();
|
||||
perform<Read_test<Block::Session::TX_QUEUE_SIZE, 1> >();
|
||||
perform<Write_test<Block::Session::TX_QUEUE_SIZE, 8, 16> >();
|
||||
perform<Violation_test>(1000);
|
||||
|
||||
PINF("Tests finished successfully!");
|
||||
} catch(Genode::Parent::Service_denied) {
|
||||
PERR("Opening block session was denied!");
|
||||
return -1;
|
||||
} catch(Test::Exception &e) {
|
||||
PERR("Test failed!");
|
||||
e.print_error();
|
||||
return -2;
|
||||
}
|
||||
return 0;
|
||||
}
|
@ -1,3 +1,3 @@
|
||||
TARGET = test-block
|
||||
TARGET = test-blk-cli
|
||||
SRC_CC = main.cc
|
||||
LIBS = base
|
149
os/src/test/blk/srv/main.cc
Normal file
149
os/src/test/blk/srv/main.cc
Normal file
@ -0,0 +1,149 @@
|
||||
/*
|
||||
* \brief Test block session interface - server side
|
||||
* \author Stefan Kalkowski
|
||||
* \date 2013-12-09
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2013 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.
|
||||
*/
|
||||
|
||||
#include <base/printf.h>
|
||||
#include <cap_session/connection.h>
|
||||
#include <timer_session/connection.h>
|
||||
#include <block/component.h>
|
||||
#include <block/driver.h>
|
||||
#include <os/config.h>
|
||||
#include <os/ring_buffer.h>
|
||||
|
||||
|
||||
class Driver : public Block::Driver
|
||||
{
|
||||
private:
|
||||
|
||||
enum { MAX_REQUESTS = 5 };
|
||||
|
||||
typedef Ring_buffer<Block::Packet_descriptor, MAX_REQUESTS,
|
||||
Ring_buffer_unsynchronized> Req_buffer;
|
||||
|
||||
Genode::size_t _number;
|
||||
Genode::size_t _size;
|
||||
Req_buffer _packets;
|
||||
Genode::Signal_dispatcher<Driver> _ack;
|
||||
Genode::Ram_dataspace_capability _blk_ds;
|
||||
unsigned char *_blk_buf;
|
||||
|
||||
void _handle_ack(unsigned)
|
||||
{
|
||||
while (!_packets.empty()) {
|
||||
Block::Packet_descriptor p = _packets.get();
|
||||
session->ack_packet(p);
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
Driver(Genode::size_t number, Genode::size_t size,
|
||||
Genode::Signal_receiver &receiver)
|
||||
: _number(number), _size(size),
|
||||
_ack(receiver, *this, &Driver::_handle_ack),
|
||||
_blk_ds(Genode::env()->ram_session()->alloc(number*size)),
|
||||
_blk_buf(Genode::env()->rm_session()->attach(_blk_ds)) {}
|
||||
|
||||
Genode::Signal_context_capability handler() { return _ack; }
|
||||
|
||||
|
||||
/*******************************
|
||||
** Block::Driver interface **
|
||||
*******************************/
|
||||
|
||||
Genode::size_t block_size() { return _size; }
|
||||
Block::sector_t block_count() { return _number; }
|
||||
|
||||
Block::Session::Operations ops()
|
||||
{
|
||||
Block::Session::Operations ops;
|
||||
ops.set_operation(Block::Packet_descriptor::READ);
|
||||
ops.set_operation(Block::Packet_descriptor::WRITE);
|
||||
return ops;
|
||||
}
|
||||
|
||||
void read(Block::sector_t block_number,
|
||||
Genode::size_t block_count,
|
||||
char *buffer,
|
||||
Block::Packet_descriptor &packet)
|
||||
{
|
||||
if (!_packets.avail_capacity())
|
||||
throw Block::Driver::Request_congestion();
|
||||
|
||||
Genode::memcpy((void*)buffer, &_blk_buf[block_number*_size],
|
||||
block_count * _size);
|
||||
_packets.add(packet);
|
||||
}
|
||||
|
||||
void write(Block::sector_t block_number,
|
||||
Genode::size_t block_count,
|
||||
const char *buffer,
|
||||
Block::Packet_descriptor &packet)
|
||||
{
|
||||
if (!_packets.avail_capacity())
|
||||
throw Block::Driver::Request_congestion();
|
||||
Genode::memcpy(&_blk_buf[block_number*_size],
|
||||
(void*)buffer, block_count * _size);
|
||||
_packets.add(packet);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct Factory : Block::Driver_factory
|
||||
{
|
||||
Genode::Signal_receiver &receiver;
|
||||
::Driver *driver;
|
||||
|
||||
Factory(Genode::Signal_receiver &r) : receiver(r)
|
||||
{
|
||||
Genode::size_t blk_nr = 1024;
|
||||
Genode::size_t blk_sz = 512;
|
||||
|
||||
try {
|
||||
Genode::config()->xml_node().attribute("sectors").value(&blk_nr);
|
||||
Genode::config()->xml_node().attribute("block_size").value(&blk_sz);
|
||||
}
|
||||
catch (...) { }
|
||||
|
||||
driver = new (Genode::env()->heap()) Driver(blk_nr, blk_sz, receiver);
|
||||
}
|
||||
|
||||
Block::Driver *create() { return driver; }
|
||||
|
||||
void destroy(Block::Driver *driver) { }
|
||||
};
|
||||
|
||||
|
||||
int main()
|
||||
{
|
||||
using namespace Genode;
|
||||
|
||||
enum { STACK_SIZE = 2048 * sizeof(Genode::addr_t) };
|
||||
static Cap_connection cap;
|
||||
static Rpc_entrypoint ep(&cap, STACK_SIZE, "test_blk_ep");
|
||||
|
||||
static Signal_receiver receiver;
|
||||
static Factory driver_factory(receiver);
|
||||
static Block::Root block_root(&ep, env()->heap(), driver_factory, receiver);
|
||||
|
||||
env()->parent()->announce(ep.manage(&block_root));
|
||||
|
||||
static Timer::Connection timer;
|
||||
timer.sigh(driver_factory.driver->handler());
|
||||
timer.trigger_periodic(10000);
|
||||
while (true) {
|
||||
Signal s = receiver.wait_for_signal();
|
||||
static_cast<Signal_dispatcher_base *>(s.context())->dispatch(s.num());
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
3
os/src/test/blk/srv/target.mk
Normal file
3
os/src/test/blk/srv/target.mk
Normal file
@ -0,0 +1,3 @@
|
||||
TARGET = test-blk-srv
|
||||
SRC_CC = main.cc
|
||||
LIBS = base config
|
@ -1,183 +0,0 @@
|
||||
/*
|
||||
* \brief Block driver interface test
|
||||
* \author Sebastian Sumpf
|
||||
* \date 2011-08-11
|
||||
*
|
||||
* Test block device, read blocks add one to the data, write block back, read
|
||||
* block again and compare outputs
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2011-2013 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.
|
||||
*/
|
||||
|
||||
#include <base/allocator_avl.h>
|
||||
#include <base/printf.h>
|
||||
#include <base/sleep.h>
|
||||
#include <base/thread.h>
|
||||
#include <block_session/connection.h>
|
||||
#include <util/string.h>
|
||||
|
||||
|
||||
static const bool read_only = false;
|
||||
|
||||
class Worker : public Genode::Thread<8192>
|
||||
{
|
||||
private:
|
||||
|
||||
Block::Connection _blk_con;
|
||||
Genode::size_t _blk_size;
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
Worker(Genode::Allocator_avl *block_alloc)
|
||||
: Thread("worker"), _blk_con(block_alloc) { }
|
||||
|
||||
void dump(Block::Packet_descriptor &p1, Block::Packet_descriptor &p2)
|
||||
{
|
||||
Block::Session::Tx::Source *source = _blk_con.tx();
|
||||
unsigned *d1 = (unsigned *)source->packet_content(p1);
|
||||
unsigned *d2 = (unsigned *)source->packet_content(p2);
|
||||
for (int i = 0; i < 128; i += 8) {
|
||||
Genode::printf("1 0x%02x: %08x %08x %08x %08x %08x %08x %08x %08x\n", i,
|
||||
d1[i], d1[i+1], d1[i+2], d1[i+3], d1[i+4], d1[i+5], d1[i+6], d1[i+7]);
|
||||
Genode::printf("2 0x%02x: %08x %08x %08x %08x %08x %08x %08x %08x\n\n", i,
|
||||
d2[i], d2[i+1], d2[i+2], d2[i+3], d2[i+4], d2[i+5], d2[i+6], d2[i+7]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void compare(Genode::size_t block, Block::Packet_descriptor &p1, Block::Packet_descriptor &p2)
|
||||
{
|
||||
using namespace Genode;
|
||||
Block::Session::Tx::Source *source = _blk_con.tx();
|
||||
char *d1 = source->packet_content(p1);
|
||||
char *d2 = source->packet_content(p2);
|
||||
|
||||
bool equal = true;
|
||||
for (size_t i = 0; i < _blk_size / sizeof(unsigned); i++)
|
||||
if (d1[i] != d2[i]) {
|
||||
equal = false;
|
||||
|
||||
if (!read_only)
|
||||
PERR("%zu: %x != %x", i, d1[i], d2[i]);
|
||||
}
|
||||
|
||||
printf("Comparing block %010zu: ", block);
|
||||
if (equal)
|
||||
printf("success\n");
|
||||
else {
|
||||
printf("failed\n");
|
||||
dump(p1, p2);
|
||||
}
|
||||
}
|
||||
|
||||
void modify(Block::Packet_descriptor &src, Block::Packet_descriptor &dst, int val)
|
||||
{
|
||||
Block::Session::Tx::Source *source = _blk_con.tx();
|
||||
for (unsigned j = 0; j < _blk_size; j++)
|
||||
source->packet_content(dst)[j] = source->packet_content(src)[j] + val;
|
||||
}
|
||||
|
||||
void submit(Block::Packet_descriptor &src,
|
||||
Block::Packet_descriptor &dst,
|
||||
int val, Genode::size_t block, bool cmp)
|
||||
{
|
||||
Block::Session::Tx::Source *source = _blk_con.tx();
|
||||
|
||||
source->submit_packet(src);
|
||||
src = source->get_acked_packet();
|
||||
|
||||
/* check for success of operation */
|
||||
if (!src.succeeded()) {
|
||||
PWRN("Could not read block %zu", block);
|
||||
return;
|
||||
}
|
||||
|
||||
if (cmp)
|
||||
compare(block, src, dst);
|
||||
|
||||
modify(src, dst, val);
|
||||
|
||||
if (read_only)
|
||||
return;
|
||||
|
||||
source->submit_packet(dst);
|
||||
dst = source->get_acked_packet();
|
||||
|
||||
/* check for success of operation */
|
||||
if (!dst.succeeded())
|
||||
PWRN("Could not write block %zu", block);
|
||||
}
|
||||
|
||||
/**
|
||||
* Thread's entry function.
|
||||
*/
|
||||
void entry()
|
||||
{
|
||||
using namespace Genode;
|
||||
|
||||
Block::Session::Tx::Source *source = _blk_con.tx();
|
||||
size_t blk_cnt = 0;
|
||||
Block::Session::Operations ops;
|
||||
_blk_con.info(&blk_cnt, &_blk_size, &ops);
|
||||
|
||||
/* check for read- and write-capability */
|
||||
if (!ops.supported(Block::Packet_descriptor::READ)) {
|
||||
PERR("Block device not readable!");
|
||||
return;
|
||||
}
|
||||
if (!ops.supported(Block::Packet_descriptor::WRITE)) {
|
||||
PERR("Block device not writeable!");
|
||||
return;
|
||||
}
|
||||
|
||||
printf("We have %zu blocks with a size of %zu bytes (%zu MB)\n",
|
||||
blk_cnt, _blk_size, blk_cnt / (2 * 1024));
|
||||
|
||||
/* now, repeatedly invert each single block of the device */
|
||||
size_t step = blk_cnt / 32;
|
||||
for (size_t i = 0; i < blk_cnt; i += step) {
|
||||
try {
|
||||
/* allocate packet-descriptor for reading */
|
||||
Block::Packet_descriptor p(source->alloc_packet(_blk_size),
|
||||
Block::Packet_descriptor::READ, i);
|
||||
|
||||
/* allocate a packet-descriptor for writing */
|
||||
Block::Packet_descriptor q(source->alloc_packet(_blk_size),
|
||||
Block::Packet_descriptor::WRITE, i);
|
||||
|
||||
submit(p, q, 1, i, false);
|
||||
submit(p, q, -1, i, true);
|
||||
|
||||
/* release packets */
|
||||
source->release_packet(q);
|
||||
source->release_packet(p);
|
||||
} catch (Block::Session::Tx::Source::Packet_alloc_failed) {
|
||||
PWRN("Mmh, strange we run out of packets");
|
||||
source->release_packet(source->get_acked_packet());
|
||||
}
|
||||
}
|
||||
|
||||
env()->parent()->close(_blk_con.cap());
|
||||
env()->parent()->exit(0);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
Genode::printf("--- AHCI block driver test ---\n");
|
||||
|
||||
Genode::Allocator_avl block_alloc(Genode::env()->heap());
|
||||
Worker th(&block_alloc);
|
||||
th.start();
|
||||
Genode::sleep_forever();
|
||||
return 0;
|
||||
}
|
@ -32,7 +32,7 @@ set build_components {
|
||||
server/terminal_log
|
||||
server/terminal_mux
|
||||
test/affinity
|
||||
test/block
|
||||
test/blk/cli
|
||||
test/gdb_monitor
|
||||
test/input
|
||||
test/lwip/http_srv
|
||||
@ -231,11 +231,11 @@ set config {
|
||||
<resource name="RAM" quantum="1M"/>
|
||||
</subsystem>
|
||||
<subsystem name="block_emmc" help="block device test on first eMMC partition">
|
||||
<binary name="test-block"/>
|
||||
<binary name="test-blk-cli"/>
|
||||
<resource name="RAM" quantum="1M" />
|
||||
</subsystem>
|
||||
<subsystem name="block_usb" help="block device test on second USB disk partition">
|
||||
<binary name="test-block"/>
|
||||
<binary name="test-blk-cli"/>
|
||||
<resource name="RAM" quantum="1M" />
|
||||
</subsystem>
|
||||
<subsystem name="vim_sata" help="VIM editor accessing the first SATA partition">
|
||||
@ -600,7 +600,7 @@ set boot_modules {
|
||||
terminal_log
|
||||
terminal_mux
|
||||
test-affinity
|
||||
test-block
|
||||
test-blk-cli
|
||||
timer
|
||||
usb_drv
|
||||
vim.tar
|
||||
|
Loading…
Reference in New Issue
Block a user