/* * \brief SD-card benchmark * \author Norman Feske * \author Martin Stein * \date 2012-07-19 */ /* * Copyright (C) 2012-2017 Genode Labs GmbH * * This file is part of the Genode OS framework, which is distributed * under the terms of the GNU Affero General Public License version 3. */ /* Genode includes */ #include #include #include /* local includes */ #include using namespace Genode; struct Benchmark { using Packet_descriptor = Block::Packet_descriptor; struct Block_operation_failed : Exception { }; enum Operation { READ, WRITE }; struct Driver_session : Block::Driver_session_base { Signal_transmitter sig; unsigned long nr_of_acks { 0 }; Driver_session(Signal_context_capability sig) : sig(sig) { } void ack_packet(Packet_descriptor &, bool success) override { if (!success) { throw Block_operation_failed(); } nr_of_acks++; sig.submit(); } }; Env &env; Platform::Connection platform { env }; Attached_rom_dataspace config { env, "config" }; Packet_descriptor pkt { }; uint64_t time_before_ms { }; Timer::Connection timer { env }; Operation operation { READ }; Signal_handler ack_handler { env.ep(), *this, &Benchmark::update_state }; Driver_session drv_session { ack_handler }; Sd_card::Driver drv { env, platform }; size_t const buf_size_kib { config.xml().attribute_value("buffer_size_kib", (size_t)0) }; size_t const buf_size { buf_size_kib * 1024 }; Attached_ram_dataspace buf { env.ram(), env.rm(), buf_size, UNCACHED }; char *buf_virt { buf.local_addr() }; addr_t buf_phys { Dataspace_client(buf.cap()) .phys_addr() }; size_t buf_off_done { 0 }; size_t buf_off_pend { 0 }; unsigned req_size_id { 0 }; size_t req_sizes[9] { 512, 1024, 1024 * 2, 1024 * 4, 1024 * 8, 1024 * 16, 1024 * 32, 1024 * 64, 1024 * 128 }; size_t req_size() const { return req_sizes[req_size_id]; } void update_state() { /* raise done counter and check if the buffer is full */ buf_off_done += drv_session.nr_of_acks * req_size(); drv_session.nr_of_acks = 0; if (buf_off_done == buf_size) { /* print stats for the current request size */ uint64_t const time_after_ms = timer.elapsed_ms(); uint64_t const duration_ms = time_after_ms - time_before_ms; size_t const kib_per_sec = (1000 * buf_size_kib) / duration_ms; log(" duration: ", duration_ms, " ms"); log(" amount: ", buf_size_kib, " KiB"); log(" throughput: ", kib_per_sec, " KiB/sec"); /* go to next request size */ buf_off_pend = 0; buf_off_done = 0; req_size_id++; /* check if we have done all request sizes for an operation */ if (req_size_id == sizeof(req_sizes)/sizeof(req_sizes[0])) { /* go to next operation or end the test */ log(""); req_size_id = 0; switch (operation) { case READ: operation = WRITE; log("-- writing to SD card --"); break; case WRITE: log("--- SD card benchmark finished ---"); return; } } log(" request size ", req_size(), " bytes"); time_before_ms = timer.elapsed_ms(); } /* issue as many requests for the current request size as possible */ try { size_t const block_size = drv.info().block_size; size_t const cnt = req_size() / block_size; for (; buf_off_pend < buf_size; buf_off_pend += req_size()) { /* calculate block offset */ addr_t const nr = buf_off_pend / block_size; if (drv.dma_enabled()) { /* request with DMA */ addr_t const phys = buf_phys + buf_off_pend; switch (operation) { case READ: drv.read_dma(nr, cnt, phys, pkt); break; case WRITE: drv.write_dma(nr, cnt, phys, pkt); break; } } else { /* request without DMA */ char *const virt = buf_virt + buf_off_pend; switch (operation) { case READ: drv.read(nr, cnt, virt, pkt); break; case WRITE: drv.write(nr, cnt, virt, pkt); break; } } } } catch (Block::Driver::Request_congestion) { } } Benchmark(Env &env) : env(env) { log(""); log("--- SD card benchmark (", drv.dma_enabled() ? "with" : "no", " DMA) ---"); drv.session(&drv_session); /* start issuing requests */ log(""); log("-- reading from SD card --"); log(" request size ", req_size(), " bytes"); time_before_ms = timer.elapsed_ms(); update_state(); } private: /* * Noncopyable */ Benchmark(Benchmark const &); Benchmark &operator = (Benchmark const &); };