From a9eecc1a2db6cc23dcd1fc9f3b19ac739b4a6067 Mon Sep 17 00:00:00 2001 From: Norman Feske Date: Wed, 4 Jan 2017 18:01:30 +0100 Subject: [PATCH] nic_loopback: API transition Ref #1987 --- repos/os/src/server/nic_loopback/main.cc | 110 +++--- repos/os/src/server/nic_loopback/target.mk | 2 +- repos/os/src/test/nic_loopback/main.cc | 436 +++++++++++++++------ 3 files changed, 355 insertions(+), 193 deletions(-) diff --git a/repos/os/src/server/nic_loopback/main.cc b/repos/os/src/server/nic_loopback/main.cc index 2fdd595a50..14a168425e 100644 --- a/repos/os/src/server/nic_loopback/main.cc +++ b/repos/os/src/server/nic_loopback/main.cc @@ -13,26 +13,24 @@ * under the terms of the GNU General Public License version 2. */ -#include -#include -#include +#include +#include #include #include #include #include #include -namespace Nic { - - class Loopback_component; +namespace Nic_loopback { + class Session_component; class Root; + class Main; + + using namespace Genode; } -namespace Server { struct Main; } - - -class Nic::Loopback_component : public Nic::Session_component +class Nic_loopback::Session_component : public Nic::Session_component { public: @@ -47,19 +45,20 @@ class Nic::Loopback_component : public Nic::Session_component * \param ep entry point used for packet stream * channels */ - Loopback_component(Genode::size_t const tx_buf_size, - Genode::size_t const rx_buf_size, - Genode::Allocator &rx_block_md_alloc, - Genode::Ram_session &ram_session, - Server::Entrypoint &ep) - : Session_component(tx_buf_size, rx_buf_size, - rx_block_md_alloc, ram_session, ep) + Session_component(size_t const tx_buf_size, + size_t const rx_buf_size, + Allocator &rx_block_md_alloc, + Ram_session &ram_session, + Entrypoint &ep) + : + Nic::Session_component(tx_buf_size, rx_buf_size, rx_block_md_alloc, + ram_session, ep) { } - Mac_address mac_address() override + Nic::Mac_address mac_address() override { char buf[] = {1,2,3,4,5,6}; - Mac_address result((void*)buf); + Nic::Mac_address result((void*)buf); return result; } @@ -73,13 +72,11 @@ class Nic::Loopback_component : public Nic::Session_component }; -void Nic::Loopback_component::_handle_packet_stream() +void Nic_loopback::Session_component::_handle_packet_stream() { - using namespace Genode; + size_t const alloc_size = Nic::Packet_allocator::DEFAULT_PACKET_SIZE; - unsigned const alloc_size = Nic::Packet_allocator::DEFAULT_PACKET_SIZE; - - /* loop unless we cannot make any progress */ + /* loop while we can make progress */ for (;;) { /* flush acknowledgements for the echoes packets */ @@ -118,22 +115,21 @@ void Nic::Loopback_component::_handle_packet_stream() Packet_descriptor packet_to_client; try { - packet_to_client = _rx.source()->alloc_packet(alloc_size); - } catch (Session::Rx::Source::Packet_alloc_failed) { - continue; - } + packet_to_client = _rx.source()->alloc_packet(alloc_size); } + catch (Session::Rx::Source::Packet_alloc_failed) { + continue; } /* obtain packet */ Packet_descriptor const packet_from_client = _tx.sink()->get_packet(); if (!packet_from_client.size()) { - Genode::warning("received zero-size packet"); + warning("received zero-size packet"); _rx.source()->release_packet(packet_to_client); continue; } - Genode::memcpy(_rx.source()->packet_content(packet_to_client), - _tx.sink()->packet_content(packet_from_client), - packet_from_client.size()); + memcpy(_rx.source()->packet_content(packet_to_client), + _tx.sink()->packet_content(packet_from_client), + packet_from_client.size()); packet_to_client = Packet_descriptor(packet_to_client.offset(), packet_from_client.size()); @@ -144,24 +140,23 @@ void Nic::Loopback_component::_handle_packet_stream() } -class Nic::Root : public Genode::Root_component +class Nic_loopback::Root : public Root_component { private: - Server::Entrypoint &_ep; + Entrypoint &_ep; + Ram_session &_ram; protected: - Loopback_component*_create_session(const char *args) + Session_component *_create_session(char const *args) { - using namespace Genode; - size_t ram_quota = Arg_string::find_arg(args, "ram_quota" ).ulong_value(0); size_t tx_buf_size = Arg_string::find_arg(args, "tx_buf_size").ulong_value(0); size_t rx_buf_size = Arg_string::find_arg(args, "rx_buf_size").ulong_value(0); /* deplete ram quota by the memory needed for the session structure */ - size_t session_size = max(4096UL, (unsigned long)sizeof(Session_component)); + size_t session_size = max(4096UL, (size_t)sizeof(Session_component)); if (ram_quota < session_size) throw Root::Quota_exceeded(); @@ -171,48 +166,39 @@ class Nic::Root : public Genode::Root_component */ if (tx_buf_size + rx_buf_size < tx_buf_size || tx_buf_size + rx_buf_size > ram_quota - session_size) { - Genode::error("insufficient 'ram_quota', got ", ram_quota, ", " - "need ", tx_buf_size + rx_buf_size + session_size); + error("insufficient 'ram_quota', got ", ram_quota, ", " + "need ", tx_buf_size + rx_buf_size + session_size); throw Root::Quota_exceeded(); } - return new (md_alloc()) Loopback_component(tx_buf_size, rx_buf_size, - *env()->heap(), - *env()->ram_session(), - _ep); + return new (md_alloc()) Session_component(tx_buf_size, rx_buf_size, + *md_alloc(), _ram, _ep); } public: - Root(Server::Entrypoint &ep, Genode::Allocator &md_alloc) + Root(Entrypoint &ep, Ram_session &ram, Allocator &md_alloc) : - Genode::Root_component(&ep.rpc_ep(), &md_alloc), - _ep(ep) + Root_component(&ep.rpc_ep(), &md_alloc), + _ep(ep), _ram(ram) { } }; -struct Server::Main +struct Nic_loopback::Main { - Entrypoint &ep; + Env &_env; - Nic::Root nic_root { ep, *Genode::env()->heap() }; + Heap _heap { _env.ram(), _env.rm() }; - Main(Entrypoint &ep) : ep(ep) + Nic_loopback::Root _root { _env.ep(), _env.ram(), _heap }; + + Main(Env &env) : _env(env) { - Genode::env()->parent()->announce(ep.manage(nic_root)); + _env.parent().announce(_env.ep().manage(_root)); } }; -namespace Server { +void Component::construct(Genode::Env &env) { static Nic_loopback::Main main(env); } - char const *name() { return "nicloop_ep"; } - - size_t stack_size() { return 16*1024*sizeof(long); } - - void construct(Entrypoint &ep) - { - static Main main(ep); - } -} diff --git a/repos/os/src/server/nic_loopback/target.mk b/repos/os/src/server/nic_loopback/target.mk index 4e15ebab19..9c63672bfa 100644 --- a/repos/os/src/server/nic_loopback/target.mk +++ b/repos/os/src/server/nic_loopback/target.mk @@ -1,3 +1,3 @@ TARGET = nic_loopback SRC_CC = main.cc -LIBS = base server +LIBS = base diff --git a/repos/os/src/test/nic_loopback/main.cc b/repos/os/src/test/nic_loopback/main.cc index b1d7bd0a94..49329f3948 100644 --- a/repos/os/src/test/nic_loopback/main.cc +++ b/repos/os/src/test/nic_loopback/main.cc @@ -5,192 +5,368 @@ */ /* - * Copyright (C) 2009-2013 Genode Labs GmbH + * Copyright (C) 2009-2016 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 #include +#include #include #include #include -#include + +namespace Test { + struct Base; + struct Roundtrip; + struct Batch; + struct Main; + + using namespace Genode; +} + namespace Genode { - static inline void print(Output &out, Packet_descriptor packet) + static void print(Output &out, Packet_descriptor const &packet) { - Genode::print(out, "offset=", packet.offset(), ", size=", packet.size()); + ::Genode::print(out, "offset=", packet.offset(), ", size=", packet.size()); } } -using namespace Genode; - - -static bool single_packet_roundtrip(Nic::Session *nic, - unsigned char content_pattern, - size_t packet_size) +struct Test::Base { - Packet_descriptor tx_packet; + public: - log("single_packet_roundtrip(content='", Char(content_pattern), "', " - "packet_size=", packet_size, ")"); + typedef String<64> Name; - /* allocate transmit packet */ - try { - tx_packet = nic->tx()->alloc_packet(packet_size); - } catch (Nic::Session::Tx::Source::Packet_alloc_failed) { - error(__func__, ": tx packet alloc failed"); - return false; - } + virtual void handle_nic() = 0; - log("allocated tx packet ", tx_packet); + private: - /* fill packet with pattern */ - char *tx_content = nic->tx()->packet_content(tx_packet); - for (unsigned i = 0; i < packet_size; i++) - tx_content[i] = content_pattern; + Env &_env; - nic->tx()->submit_packet(tx_packet); + Name const _name; - /* wait for acknowledgement */ - Packet_descriptor ack_tx_packet = nic->tx()->get_acked_packet(); + Signal_context_capability _succeeded_sigh; - if (ack_tx_packet.size() != tx_packet.size() - || ack_tx_packet.offset() != tx_packet.offset()) { - error("unexpected acked packet"); - return false; - } + bool _done = false; - /* - * Because we sent the packet to a loop-back device, we expect - * the same packet to be available at the rx channel of the NIC - * session. - */ - Packet_descriptor rx_packet = nic->rx()->get_packet(); - log("received rx packet ", rx_packet); + Heap _heap { _env.ram(), _env.rm() }; - if (rx_packet.size() != tx_packet.size()) { - error("sent and echoed packets differ in size"); - return false; - } + Allocator_avl _tx_block_alloc { &_heap }; - /* compare original and echoed packets */ - char *rx_content = nic->rx()->packet_content(rx_packet); - for (unsigned i = 0; i < packet_size; i++) - if (rx_content[i] != tx_content[i]) { - error("sent and echoed packets have differnt content"); - return false; + enum { BUF_SIZE = Nic::Packet_allocator::DEFAULT_PACKET_SIZE * 128 }; + + Nic::Connection _nic { &_tx_block_alloc, BUF_SIZE, BUF_SIZE }; + + void _handle_nic() { if (!_done) handle_nic(); } + + Signal_handler _nic_handler { _env.ep(), *this, &Base::_handle_nic }; + + public: + + Nic::Connection &nic() { return _nic; } + + void success() + { + /* + * There may still be packet-stream signals in flight, which we can + * ignore once the test succeeded. + */ + _done = true; + + log("-- ", _name, " test succeeded --"); + + Signal_transmitter(_succeeded_sigh).submit(); } - /* acknowledge received packet */ - nic->rx()->acknowledge_packet(rx_packet); + template + static void abort(ARGS &&... args) + { + error(args...); + class Error : Exception { }; + throw Error(); + } - /* release sent packet to free the space in the tx communication buffer */ - nic->tx()->release_packet(tx_packet); + Base(Env &env, Name const &name, Signal_context_capability succeeded_sigh) + : + _env(env), _name(name), _succeeded_sigh(succeeded_sigh) + { + log("-- starting ", _name, " test --"); - return true; -} + _nic.tx_channel()->sigh_ready_to_submit(_nic_handler); + _nic.tx_channel()->sigh_ack_avail (_nic_handler); + _nic.rx_channel()->sigh_ready_to_ack (_nic_handler); + _nic.rx_channel()->sigh_packet_avail (_nic_handler); + } +}; -static bool batch_packets(Nic::Session *nic, unsigned num_packets) +struct Test::Roundtrip : Base { - unsigned tx_cnt = 0, acked_cnt = 0, rx_cnt = 0, batch_cnt = 0; + /* + * Each character of the string is used as pattern for one iteration. + */ + typedef String<16> Patterns; - Genode::Signal_context tx_ready_to_submit, tx_ack_avail, - rx_ready_to_ack, rx_packet_avail; - Genode::Signal_receiver signal_receiver; + Patterns const _patterns; - nic->tx_channel()->sigh_ready_to_submit (signal_receiver.manage(&tx_ready_to_submit)); - nic->tx_channel()->sigh_ack_avail (signal_receiver.manage(&tx_ack_avail)); - nic->rx_channel()->sigh_ready_to_ack (signal_receiver.manage(&rx_ready_to_ack)); - nic->rx_channel()->sigh_packet_avail (signal_receiver.manage(&rx_packet_avail)); + unsigned _cnt = 0; + + off_t _expected_packet_offset = ~0L; + + char _pattern() const { return _patterns.string()[_cnt]; } + + bool _received_acknowledgement = false; + bool _received_reflected_packet = false; enum { PACKET_SIZE = 100 }; - while (acked_cnt != num_packets - || tx_cnt != num_packets - || rx_cnt != num_packets) { + void _produce_packet(Nic::Connection &nic) + { + log("start iteration ", _cnt, " with pattern '", Char(_pattern()), "'"); - if (tx_cnt > rx_cnt || tx_cnt > acked_cnt) - signal_receiver.wait_for_signal(); + Packet_descriptor tx_packet; - /* produce as many packets as possible as one batch */ - unsigned max_outstanding_requests = Nic::Session::QUEUE_SIZE - 1; - while (nic->tx()->ready_to_submit() - && tx_cnt < num_packets - && tx_cnt - rx_cnt < max_outstanding_requests) { + /* allocate tx packet */ + try { + tx_packet = nic.tx()->alloc_packet(PACKET_SIZE); } + catch (Nic::Session::Tx::Source::Packet_alloc_failed) { + abort(__func__, ": tx packet alloc failed"); } - try { - Packet_descriptor tx_packet = nic->tx()->alloc_packet(PACKET_SIZE); - nic->tx()->submit_packet(tx_packet); - tx_cnt++; - } catch (Nic::Session::Tx::Source::Packet_alloc_failed) { - break; - } - } + /* + * Remember the packet offset of the first packet. The offsets + * of all subsequent packets are expected to be the same. + */ + if (_expected_packet_offset == ~0L) + _expected_packet_offset = tx_packet.offset(); - unsigned batch_rx_cnt = 0, batch_acked_cnt = 0; + log("allocated tx packet ", tx_packet); - /* check for acknowledgements */ - while (nic->tx()->ack_avail()) { - Packet_descriptor acked_packet = nic->tx()->get_acked_packet(); - nic->tx()->release_packet(acked_packet); - acked_cnt++; - batch_acked_cnt++; - } + /* fill packet with pattern */ + char *tx_content = nic.tx()->packet_content(tx_packet); + for (unsigned i = 0; i < PACKET_SIZE; i++) + tx_content[i] = _pattern(); - /* check for available rx packets */ - while (nic->rx()->packet_avail() && nic->rx()->ready_to_ack()) { - Packet_descriptor rx_packet = nic->rx()->get_packet(); + if (!nic.tx()->ready_to_submit()) + abort(__func__, ": submit queue is unexpectedly full"); - if (!nic->rx()->ready_to_ack()) - warning("not ready for ack, going to blocK"); - - nic->rx()->acknowledge_packet(rx_packet); - rx_cnt++; - batch_rx_cnt++; - } - - log("acked ", batch_acked_cnt, " packets, " - "received ", batch_rx_cnt, " packets " - "-> tx: ", tx_cnt, ", acked: ", acked_cnt, ", rx: ", rx_cnt); - - batch_cnt++; + nic.tx()->submit_packet(tx_packet); } - log("test used ", batch_cnt, " batches"); - return true; -} + void _consume_and_compare_packet(Nic::Connection &nic) + { + /* + * The acknowledgement for the sent packet and the reflected packet + * may arrive in any order. + */ + + if (nic.tx()->ack_avail()) { + + /* wait for acknowledgement */ + Packet_descriptor const ack_tx_packet = nic.tx()->get_acked_packet(); + + if (ack_tx_packet.size() != PACKET_SIZE) + abort(__func__, ": unexpected acked packet"); + + if (ack_tx_packet.offset() != _expected_packet_offset) + abort(__func__, ": unexpected offset of acknowledged packet"); + + /* release sent packet to free the space in the tx communication buffer */ + nic.tx()->release_packet(ack_tx_packet); + + _received_acknowledgement = true; + } + + if (nic.rx()->packet_avail()) { + + /* + * Because we sent the packet to a loop-back device, we expect + * the same packet to be available at the rx channel of the NIC + * session. + */ + Packet_descriptor const rx_packet = nic.rx()->get_packet(); + log("received rx packet ", rx_packet); + + if (rx_packet.size() != PACKET_SIZE) + abort("sent and echoed packets differ in size"); + + if (rx_packet.offset() != _expected_packet_offset) + abort(__func__, ": unexpected offset of received packet"); + + /* compare original and echoed packets */ + char const * const rx_content = nic.rx()->packet_content(rx_packet); + for (unsigned i = 0; i < PACKET_SIZE; i++) + if (rx_content[i] != _pattern()) { + log("rx_content[", i, "]: ", Char(rx_content[i])); + log("pattern: ", Char(_pattern())); + abort(__func__, ":sent and echoed packets have different content"); + } + + if (!nic.rx()->ack_slots_free()) + abort(__func__, ": acknowledgement queue is unexpectedly full"); + + nic.rx()->acknowledge_packet(rx_packet); + + _received_reflected_packet = true; + } + } + + void handle_nic() override + { + _consume_and_compare_packet(nic()); + + if (!_received_acknowledgement || !_received_reflected_packet) + return; + + /* start next iteration */ + _cnt++; + + /* check if we reached the end of the pattern string */ + if (_pattern() == 0) { + success(); + return; + } + + _received_reflected_packet = false; + _received_acknowledgement = false; + _produce_packet(nic()); + } + + Roundtrip(Env &env, Signal_context_capability success_sigh, Patterns patterns) + : + Base(env, "roundtrip", success_sigh), _patterns(patterns) + { + _produce_packet(nic()); + } +}; -int main(int, char **) +struct Test::Batch : Base { - log("--- NIC loop-back test ---"); + unsigned const _num_packets; - enum { BUF_SIZE = Nic::Packet_allocator::DEFAULT_PACKET_SIZE * 128 }; + unsigned _tx_cnt = 0, _acked_cnt = 0, _rx_cnt = 0, _batch_cnt = 0; - bool config_test_roundtrip = true; - bool config_test_batch = true; + enum { PACKET_SIZE = 100 }; - if (config_test_roundtrip) { - log("-- test roundtrip two times (packet offsets should be the same) --"); - Allocator_avl tx_block_alloc(env()->heap()); - Nic::Connection nic(&tx_block_alloc, BUF_SIZE, BUF_SIZE); - single_packet_roundtrip(&nic, 'a', 100); - single_packet_roundtrip(&nic, 'b', 100); + static unsigned _send_packets(Nic::Connection &nic, unsigned limit) + { + unsigned cnt = 0; + while (nic.tx()->ready_to_submit() && cnt < limit) { + try { + Packet_descriptor tx_packet = nic.tx()->alloc_packet(PACKET_SIZE); + nic.tx()->submit_packet(tx_packet); + cnt++; + } + catch (Nic::Session::Tx::Source::Packet_alloc_failed) { break; } + } + return cnt; } - if (config_test_batch) { - log("-- test submitting and receiving batches of packets --"); - Allocator_avl tx_block_alloc(env()->heap()); - Nic::Connection nic(&tx_block_alloc, BUF_SIZE, BUF_SIZE); - enum { NUM_PACKETS = 1000 }; - batch_packets(&nic, NUM_PACKETS); + /* + * \return number of received acknowledgements + */ + static unsigned _collect_acknowledgements(Nic::Connection &nic) + { + unsigned cnt = 0; + while (nic.tx()->ack_avail()) { + Packet_descriptor acked_packet = nic.tx()->get_acked_packet(); + nic.tx()->release_packet(acked_packet); + cnt++; + } + return cnt; } - log("--- finished NIC loop-back test ---"); - return 0; -} + static unsigned _receive_all_incoming_packets(Nic::Connection &nic) + { + unsigned cnt = 0; + while (nic.rx()->packet_avail() && nic.rx()->ready_to_ack()) { + Packet_descriptor rx_packet = nic.rx()->get_packet(); + nic.rx()->acknowledge_packet(rx_packet); + cnt++; + } + return cnt; + } + + void _check_for_success() + { + unsigned const n = _num_packets; + if (_acked_cnt == n && _tx_cnt == n && _rx_cnt == n) + success(); + } + + void handle_nic() override + { + unsigned const max_outstanding_requests = Nic::Session::QUEUE_SIZE - 1; + unsigned const outstanding_requests = _tx_cnt - _rx_cnt; + + unsigned const tx_limit = min(_num_packets - _tx_cnt, + max_outstanding_requests - outstanding_requests); + + unsigned const num_tx = _send_packets(nic(), tx_limit); + unsigned const num_acks = _collect_acknowledgements(nic()); + unsigned const num_rx = _receive_all_incoming_packets(nic()); + + _tx_cnt += num_tx; + _rx_cnt += num_rx; + _acked_cnt += num_acks; + + log("acked ", num_acks, " packets, " + "received ", num_rx, " packets " + "-> tx: ", _tx_cnt, ", acked: ", _acked_cnt, ", rx: ", _rx_cnt); + + _check_for_success(); + } + + Batch(Env &env, Signal_context_capability success_sigh, unsigned num_packets) + : + Base(env, "batch", success_sigh), _num_packets(num_packets) + { + handle_nic(); + } +}; + + +struct Test::Main +{ + Env &_env; + + Constructible _roundtrip; + Constructible _batch; + + Signal_handler
_test_completed_handler { + _env.ep(), *this, &Main::_handle_test_completed }; + + void _handle_test_completed() + { + if (_roundtrip.constructed()) { + _roundtrip.destruct(); + + enum { NUM_PACKETS = 1000 }; + _batch.construct(_env, _test_completed_handler, NUM_PACKETS); + return; + } + + if (_batch.constructed()) { + _batch.destruct(); + log("--- finished NIC loop-back test ---"); + _env.parent().exit(0); + } + } + + Main(Env &env) : _env(env) + { + log("--- NIC loop-back test ---"); + + _roundtrip.construct(_env, _test_completed_handler, "abcdefghijklmn"); + } +}; + + +void Component::construct(Genode::Env &env) { static Test::Main main(env); } +