diff --git a/os/run/ahci_bench.run b/os/run/ahci_bench.run index 745fd28b8..145e6f3cc 100644 --- a/os/run/ahci_bench.run +++ b/os/run/ahci_bench.run @@ -1,66 +1,120 @@ + +# +# Select benchmark layer +# +# 0: driver internal +# 1: native Genode app +# 2: native Genode app with partition manager +# +set layer 0 + # # Build # - -build { +set build_components { core init drivers/timer drivers/ahci drivers/platform } -create_boot_directory +lappend_if [expr ($layer == 1 || $layer == 2)] build_components test/block_bench +lappend_if [expr ($layer == 2)] build_components server/part_blk +build $build_components + +create_boot_directory # # Config # -install_config { - - - - - - - - - - - - - - - - +# basic config for all layers +set config { + + + + + + + + + + + + + + + + + + + + + + + + } - - - - - - - - - - - - - -} +# start driver internal bench (wich a the driver itself) with layer 0 +append_if [expr $layer == 0] config { + + + + } +# start part_blk with layer 1 +append_if [expr $layer == 2] config { + + + + + + + + + + + } + +# start normal AHCI driver and bench app with layer 1 or 2 +append_if [expr ($layer == 1 || $layer == 2)] config { + + + + + + } + +# if layer is 2 route block requests of bench app to part_blk +append_if [expr $layer == 2] config { + + + + } + +# end start node of bench app if layer 1 or 2 +append_if [expr ($layer == 1 || $layer == 2)] config { + } + +# end config +append config { + } + +install_config $config # # Boot modules # -build_boot_image { - core init - timer - platform_drv - ahci_bench -} +set boot_modules { core init timer platform_drv } + +lappend_if [expr ($layer == 0)] boot_modules ahci_bench +lappend_if [expr ($layer == 1 || $layer == 2)] boot_modules ahci +lappend_if [expr ($layer == 1 || $layer == 2)] boot_modules test-block_bench +lappend_if [expr ($layer == 2)] boot_modules part_blk + +build_boot_image $boot_modules run_genode_until forever -# vi: set ft=tcl : diff --git a/os/src/test/block_bench/main.cc b/os/src/test/block_bench/main.cc new file mode 100644 index 000000000..3f8344781 --- /dev/null +++ b/os/src/test/block_bench/main.cc @@ -0,0 +1,213 @@ +/* + * \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. + */ + +/* Genode includes */ +#include +#include +#include +#include +#include +#include + +using namespace Genode; + +enum { VERBOSE = 0 }; + +/* + * Do a bench for a specific access command and request size + * + * \param timer timer for measurements + * \param request_size number of bytes per request + * \param source block session packet sink + * \param block_size block size of the targeted device + * \param max_lba maximum logical block address of the device + */ +static void run_benchmark( + Timer::Session & timer, + Genode::size_t request_size, + Block::Session::Tx::Source * source, + size_t block_size, + unsigned max_lba, + void * buf, + bool write) +{ + /* + * request_size: how many bytes are accessed by one command + * block_size: raw block size that is used by the drive (not the software) + * block_count: how many raw blocks are accessed by one command + */ + size_t const block_count = request_size / block_size; + /* + * The goal is to get 5 test repetitions that took 2 s < time < 2.3 s, + * each. Thus we start with count = 32 and then adjust the count + * for a retry if the test time is not in range. + */ + unsigned tmp_bytes = 64 * request_size;; + unsigned bytes = 0; + unsigned ms = 0; + unsigned reps = 0; + float sec = 0; + float mb_per_sec = 0; + while (1) + { + size_t num_requests = tmp_bytes / request_size; + if ((num_requests * block_count) >= max_lba) { PERR("X"); while(1); } + + /* do measurement */ + unsigned const time_before_ms = timer.elapsed_ms(); + for (unsigned i = 0; i < num_requests; i++) + { + /* create packet */ + addr_t const lba = i * block_count; + Block::Packet_descriptor p( + source->alloc_packet(request_size), + write ? Block::Packet_descriptor::WRITE : + Block::Packet_descriptor::READ, + lba, block_count); + + /* simulate payload */ + void * pptr = source->packet_content(p); + if (write) memcpy(pptr, buf, request_size); + + /* submit packet */ + source->submit_packet(p); + p = source->get_acked_packet(); + if (!p.succeeded()) { + PERR("could not access block %lu", lba); + while (1) ; + } + /* simulate payload */ + if (!write) memcpy(buf, pptr, request_size); + + /* release packet */ + source->release_packet(p); + } + /* read results */ + unsigned const time_after_ms = timer.elapsed_ms(); + ms = time_after_ms - time_before_ms; + + /* trial log */ + if (VERBOSE) + printf("%s %u bytes in %u ms\n", + write ? "written" : "read", tmp_bytes, ms); + + /* check if test time in range */ + if (ms < 2000 || ms >= 2300) { + /* + * adjust transfer amount according to measured time + * + * FIXME implement static inertia + */ + tmp_bytes = (((float) 2150 / ms) * tmp_bytes); + tmp_bytes &= 0xfffffe00; /* align to 512 byte blocks */ + } else { + /* if new result is better than the last one do update */ + float const tmp_mb = ((float)tmp_bytes / 1000) / 1000; + float const tmp_sec = (float)ms / 1000; + float const tmp_mb_per_sec = (float)tmp_mb / tmp_sec; + if (tmp_mb_per_sec > mb_per_sec) { + sec = tmp_sec; + mb_per_sec = tmp_mb_per_sec; + bytes = tmp_bytes; + } + /* check if we need more repetitions */ + reps++; + if (reps == 5) break; + } + } + unsigned const sec_left = sec; + unsigned const sec_right = 1000 * ((float)sec - sec_left); + unsigned const mb_per_sec_left = mb_per_sec; + unsigned const mb_per_sec_right = 1000 * ((float)mb_per_sec - mb_per_sec_left); + PLOG(" %10u %10u %u.%03u %10u.%03u", request_size, bytes, sec_left, sec_right, mb_per_sec_left, mb_per_sec_right); +} + +void print_bench_head() +{ + printf("\n"); + printf("bytes/block bytes sec MB/sec\n"); + printf("----------------------------------------------\n"); +} + +int main(int argc, char **argv) +{ + using namespace Genode; + + printf("AHCI bench\n"); + printf("==========\n"); + + enum { TX_BUF_SIZE = 2 * 1024 * 1024 }; + + /* get block connection */ + Genode::Allocator_avl block_alloc(Genode::env()->heap()); + Block::Connection _blk_con(&block_alloc, TX_BUF_SIZE); + Block::Session::Tx::Source * source = _blk_con.tx(); + + /* check block-connection info */ + Genode::size_t _blk_size = 0; + size_t blk_cnt = 0; + Block::Session::Operations ops; + _blk_con.info(&blk_cnt, &_blk_size, &ops); + if (!ops.supported(Block::Packet_descriptor::READ)) { + PERR("Block device not readable!"); + while(1); } + if (!ops.supported(Block::Packet_descriptor::WRITE)) { + PERR("Block device not writeable!"); + while(1); } + + /* data for payload simulation */ + enum { BUF_SIZE = 1 * 1024 * 1024 }; + void * buf = env()->heap()->alloc(BUF_SIZE); + for (unsigned o = 0; o < BUF_SIZE; o += sizeof(unsigned)) + *(unsigned volatile *)((addr_t)buf + o) = 0x12345678; + + static Timer::Connection timer; + + long const request_sizes[] = { + 1048576, 262144, 16384, 8192, 4096, 2048, 1024, 512, 0 }; + + /* + * Benchmark reading from SATA device + */ + + printf("\n"); + printf("read\n"); + printf("~~~~\n"); + print_bench_head(); + + for (unsigned i = 0; request_sizes[i]; i++) + run_benchmark(timer, request_sizes[i], source, _blk_size, blk_cnt, buf, 0); + + /* + * Benchmark writing to SATA device + * + * Attention: Original data will be overridden on target drive + */ + + printf("\n"); + printf("write\n"); + printf("~~~~~\n"); + print_bench_head(); + + for (unsigned i = 0; request_sizes[i]; i++) + run_benchmark(timer, request_sizes[i], source, _blk_size, blk_cnt, buf, 1); + + printf("\n"); + printf("benchmark finished\n"); + sleep_forever(); + return 0; +} + diff --git a/os/src/test/block_bench/target.mk b/os/src/test/block_bench/target.mk new file mode 100644 index 000000000..1ae545300 --- /dev/null +++ b/os/src/test/block_bench/target.mk @@ -0,0 +1,3 @@ +TARGET = test-block_bench +SRC_CC = main.cc +LIBS = base