diff --git a/repos/os/include/os/packet_stream.h b/repos/os/include/os/packet_stream.h index 9159cf30a0..e6c252ea03 100644 --- a/repos/os/include/os/packet_stream.h +++ b/repos/os/include/os/packet_stream.h @@ -144,7 +144,7 @@ class Genode::Packet_descriptor Genode::off_t offset() const { return _offset; } Genode::size_t size() const { return _size; } - bool valid() { return _size != 0; } + bool valid() const { return _size != 0; } }; diff --git a/repos/os/run/nic_loopback.run b/repos/os/run/nic_loopback.run new file mode 100644 index 0000000000..feba9e34a4 --- /dev/null +++ b/repos/os/run/nic_loopback.run @@ -0,0 +1,63 @@ +# +# Build +# + +set build_components { + core init + test/nic_loopback + server/nic_loopback +} + +build $build_components + +create_boot_directory + +# +# Generate config +# + +append config { + + + + + + + + + + + + + + + + + + + + + + + + +} + +install_config $config + +# +# Boot modules +# + +# generic modules +set boot_modules { + core init + nic_loopback + test-nic_loopback +} + +build_boot_image $boot_modules + +append qemu_args " -nographic -serial mon:stdio -m 256 " + +run_genode_until {child .* exited with exit value 0.*} 60 diff --git a/repos/os/src/server/nic_loopback/main.cc b/repos/os/src/server/nic_loopback/main.cc index 665b9e63b6..14616d7778 100644 --- a/repos/os/src/server/nic_loopback/main.cc +++ b/repos/os/src/server/nic_loopback/main.cc @@ -15,205 +15,244 @@ #include #include -#include +#include +#include +#include #include #include #include #include #include -#include - - namespace Nic { - class Communication_buffer : Genode::Ram_dataspace_capability - { - public: - - Communication_buffer(Genode::size_t size) - : Genode::Ram_dataspace_capability(Genode::env()->ram_session()->alloc(size)) - { } - - ~Communication_buffer() { Genode::env()->ram_session()->free(*this); } - - Genode::Dataspace_capability dataspace() { return *this; } - }; - - - class Tx_rx_communication_buffers - { - private: - - Communication_buffer _tx_buf, _rx_buf; - - public: - - Tx_rx_communication_buffers(Genode::size_t tx_size, - Genode::size_t rx_size) - : _tx_buf(tx_size), _rx_buf(rx_size) { } - - Genode::Dataspace_capability tx_ds() { return _tx_buf.dataspace(); } - Genode::Dataspace_capability rx_ds() { return _rx_buf.dataspace(); } - }; - - - enum { STACK_SIZE = 8*1024 }; - class Session_component : private Genode::Allocator_avl, - private Tx_rx_communication_buffers, - private Genode::Thread, - public Session_rpc_object - { - private: - - /** - * Packet-handling thread - */ - void entry() - { - using namespace Genode; - - for (;;) { - Packet_descriptor packet_from_client, packet_to_client; - - /* get packet, block until a packet is available */ - packet_from_client = _tx.sink()->get_packet(); - - if (!packet_from_client.valid()) { - PWRN("received invalid packet"); - continue; - } - - size_t packet_size = packet_from_client.size(); - try { - packet_to_client = _rx.source()->alloc_packet(packet_size); - - Genode::memcpy(_rx.source()->packet_content(packet_to_client), - _tx.sink()->packet_content(packet_from_client), - packet_size); - - _rx.source()->submit_packet(packet_to_client); - - } catch (Session::Rx::Source::Packet_alloc_failed) { - PWRN("transmit packet allocation failed, drop packet"); - } - - if (!_tx.sink()->ready_to_ack()) - printf("need to wait until ready-for-ack\n"); - - _tx.sink()->acknowledge_packet(packet_from_client); - - /* flush acknowledgements for the echoes packets */ - while (_rx.source()->ack_avail()) - _rx.source()->release_packet(_rx.source()->get_acked_packet()); - } - } - - public: - - /** - * Constructor - * - * \param tx_buf_size buffer size for tx channel - * \param rx_buf_size buffer size for rx channel - * \param rx_block_md_alloc backing store of the meta data of the - * rx block allocator - * \param ep entry point used for packet stream - * channels - */ - Session_component(Genode::size_t tx_buf_size, - Genode::size_t rx_buf_size, - Genode::Allocator *rx_block_md_alloc, - Genode::Rpc_entrypoint &ep) - : - Genode::Allocator_avl(rx_block_md_alloc), - Tx_rx_communication_buffers(tx_buf_size, rx_buf_size), - Thread("nic_packet_handler"), - Session_rpc_object(Tx_rx_communication_buffers::tx_ds(), - Tx_rx_communication_buffers::rx_ds(), - static_cast(this), ep) - { - /* start packet-handling thread */ - start(); - } - - Mac_address mac_address() - { - Mac_address result = {{1,2,3,4,5,6}}; - return result; - } - - bool link_state() - { - /* XXX always return true, for now */ - return true; - } - - void link_state_sigh(Genode::Signal_context_capability sigh) { } - }; - - class Root : public Genode::Root_component - { - private: - - Genode::Rpc_entrypoint &_channel_ep; - - protected: - - Session_component *_create_session(const char *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)); - if (ram_quota < session_size) - throw Root::Quota_exceeded(); - - /* - * Check if donated ram quota suffices for both communication - * buffers. Also check both sizes separately to handle a - * possible overflow of the sum of both sizes. - */ - if (tx_buf_size > ram_quota - session_size - || rx_buf_size > ram_quota - session_size - || tx_buf_size + rx_buf_size > ram_quota - session_size) { - PERR("insufficient 'ram_quota', got %zd, need %zd", - ram_quota, tx_buf_size + rx_buf_size + session_size); - throw Root::Quota_exceeded(); - } - - return new (md_alloc()) Session_component(tx_buf_size, rx_buf_size, - env()->heap(), _channel_ep); - } - - public: - - Root(Genode::Rpc_entrypoint *session_ep, - Genode::Allocator *md_alloc) - : - Genode::Root_component(session_ep, md_alloc), - _channel_ep(*session_ep) - { } - }; + class Communication_buffers; + class Session_component; + class Root; } -using namespace Genode; +namespace Server { struct Main; } -int main(int, char **) + +class Nic::Communication_buffers { - enum { STACK_SIZE = 2*4096 }; - static Cap_connection cap; - static Rpc_entrypoint ep(&cap, STACK_SIZE, "nicloop_ep"); + protected: - static Nic::Root nic_root(&ep, env()->heap()); - env()->parent()->announce(ep.manage(&nic_root)); + Genode::Allocator_avl _rx_packet_alloc; - sleep_forever(); - return 0; + Genode::Attached_ram_dataspace _tx_ds, _rx_ds; + + Communication_buffers(Genode::Allocator &rx_block_md_alloc, + Genode::Ram_session &ram_session, + Genode::size_t tx_size, Genode::size_t rx_size) + : + _rx_packet_alloc(&rx_block_md_alloc), + _tx_ds(&ram_session, tx_size), + _rx_ds(&ram_session, rx_size) + { } +}; + + +class Nic::Session_component : Communication_buffers, public Session_rpc_object +{ + private: + + Server::Entrypoint &_ep; + + void _handle_packet_stream(unsigned); + + Genode::Signal_rpc_member _packet_stream_dispatcher { + _ep, *this, &Session_component::_handle_packet_stream }; + + public: + + /** + * Constructor + * + * \param tx_buf_size buffer size for tx channel + * \param rx_buf_size buffer size for rx channel + * \param rx_block_md_alloc backing store of the meta data of the + * rx block allocator + * \param ram_session RAM session to allocate tx and rx buffers + * \param ep entry point used for packet stream + * channels + */ + Session_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) + : + Communication_buffers(rx_block_md_alloc, ram_session, + tx_buf_size, rx_buf_size), + Session_rpc_object(this->_tx_ds.cap(), + this->_rx_ds.cap(), + &this->_rx_packet_alloc, ep.rpc_ep()), + _ep(ep) + { + /* install data-flow signal handlers for both packet streams */ + _tx.sigh_ready_to_ack(_packet_stream_dispatcher); + _tx.sigh_packet_avail(_packet_stream_dispatcher); + _rx.sigh_ready_to_submit(_packet_stream_dispatcher); + _rx.sigh_ack_avail(_packet_stream_dispatcher); + } + + Mac_address mac_address() + { + Mac_address result = {{1,2,3,4,5,6}}; + return result; + } + + bool link_state() + { + /* XXX always return true, for now */ + return true; + } + + void link_state_sigh(Genode::Signal_context_capability sigh) { } +}; + + +void Nic::Session_component::_handle_packet_stream(unsigned) +{ + using namespace Genode; + + /* loop unless we cannot make any progress */ + for (;;) { + + /* flush acknowledgements for the echoes packets */ + while (_rx.source()->ack_avail()) + _rx.source()->release_packet(_rx.source()->get_acked_packet()); + + /* + * If the client cannot accept new acknowledgements for a sent packets, + * we won't consume the sent packet. + */ + if (!_tx.sink()->ready_to_ack()) + return; + + /* + * Nothing to be done if the client has not sent any packets. + */ + if (!_tx.sink()->packet_avail()) + return; + + /* + * Here we know that the client has submitted a packet to us and is also + * able it receive the corresponding acknowledgement. + */ + + /* + * The client fails to pick up the packets from the rx channel. So we + * won't try to submit new packets. + */ + if (!_rx.source()->ready_to_submit()) + return; + + /* + * We are safe to process one packet without blocking. + */ + + /* obtain packet */ + Packet_descriptor const packet_from_client = _tx.sink()->get_packet(); + if (!packet_from_client.valid()) { + PWRN("received invalid packet"); + continue; + } + + try { + size_t const packet_size = packet_from_client.size(); + + Packet_descriptor const packet_to_client = + _rx.source()->alloc_packet(packet_size); + + Genode::memcpy(_rx.source()->packet_content(packet_to_client), + _tx.sink()->packet_content(packet_from_client), + packet_size); + + _rx.source()->submit_packet(packet_to_client); + + } catch (Session::Rx::Source::Packet_alloc_failed) { + PWRN("transmit packet allocation failed, drop packet"); + } + + _tx.sink()->acknowledge_packet(packet_from_client); + } } + +class Nic::Root : public Genode::Root_component +{ + private: + + Server::Entrypoint &_ep; + + protected: + + Session_component *_create_session(const char *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)); + if (ram_quota < session_size) + throw Root::Quota_exceeded(); + + /* + * Check if donated ram quota suffices for both communication + * buffers. Also check both sizes separately to handle a + * possible overflow of the sum of both sizes. + */ + if (tx_buf_size > ram_quota - session_size + || rx_buf_size > ram_quota - session_size + || tx_buf_size + rx_buf_size > ram_quota - session_size) { + PERR("insufficient 'ram_quota', got %zd, need %zd", + ram_quota, tx_buf_size + rx_buf_size + session_size); + throw Root::Quota_exceeded(); + } + + return new (md_alloc()) Session_component(tx_buf_size, rx_buf_size, + *env()->heap(), + *env()->ram_session(), + _ep); + } + + public: + + Root(Server::Entrypoint &ep, Genode::Allocator &md_alloc) + : + Genode::Root_component(&ep.rpc_ep(), &md_alloc), + _ep(ep) + { } +}; + + +struct Server::Main +{ + Entrypoint &ep; + + Nic::Root nic_root { ep, *Genode::env()->heap() }; + + Main(Entrypoint &ep) : ep(ep) + { + Genode::env()->parent()->announce(ep.manage(nic_root)); + } +}; + + +namespace Server { + + char const *name() { return "nicloop_ep"; } + + size_t stack_size() { return 2*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 9c63672bfa..4e15ebab19 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 +LIBS = base server diff --git a/tool/autopilot.list b/tool/autopilot.list index 6e5670a66b..038ded6fe1 100644 --- a/tool/autopilot.list +++ b/tool/autopilot.list @@ -52,3 +52,4 @@ vmm bomb cpu_quota stdcxx +nic_loopback