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