diff --git a/repos/os/recipes/pkg/black_hole/runtime b/repos/os/recipes/pkg/black_hole/runtime
index 5b2ab031bb..2c41dfc4cc 100644
--- a/repos/os/recipes/pkg/black_hole/runtime
+++ b/repos/os/recipes/pkg/black_hole/runtime
@@ -6,6 +6,7 @@
+
@@ -14,6 +15,7 @@
+
diff --git a/repos/os/recipes/pkg/test-black_hole/runtime b/repos/os/recipes/pkg/test-black_hole/runtime
index 597e59ac8e..3711cd5408 100644
--- a/repos/os/recipes/pkg/test-black_hole/runtime
+++ b/repos/os/recipes/pkg/test-black_hole/runtime
@@ -30,6 +30,7 @@
+
@@ -37,6 +38,7 @@
+
@@ -55,6 +57,7 @@
+
diff --git a/repos/os/recipes/src/black_hole/used_apis b/repos/os/recipes/src/black_hole/used_apis
index a40b53f092..d9b80bac1d 100644
--- a/repos/os/recipes/src/black_hole/used_apis
+++ b/repos/os/recipes/src/black_hole/used_apis
@@ -4,4 +4,5 @@ audio_out_session
capture_session
event_session
nic_session
+uplink_session
os
diff --git a/repos/os/recipes/src/test-black_hole/used_apis b/repos/os/recipes/src/test-black_hole/used_apis
index 1474ce00c2..53873ce068 100644
--- a/repos/os/recipes/src/test-black_hole/used_apis
+++ b/repos/os/recipes/src/test-black_hole/used_apis
@@ -6,3 +6,4 @@ audio_out_session
event_session
capture_session
nic_session
+uplink_session
diff --git a/repos/os/src/server/black_hole/README b/repos/os/src/server/black_hole/README
index 66f07dd720..d08e5a6737 100644
--- a/repos/os/src/server/black_hole/README
+++ b/repos/os/src/server/black_hole/README
@@ -9,6 +9,7 @@ in the configuration of the component:
* Capture
* Event
* Nic
+* Uplink
@@ -16,4 +17,5 @@ in the configuration of the component:
+
diff --git a/repos/os/src/server/black_hole/config.xsd b/repos/os/src/server/black_hole/config.xsd
index 3919c4653e..df2da3f308 100644
--- a/repos/os/src/server/black_hole/config.xsd
+++ b/repos/os/src/server/black_hole/config.xsd
@@ -4,6 +4,7 @@
+
diff --git a/repos/os/src/server/black_hole/main.cc b/repos/os/src/server/black_hole/main.cc
index c1f78bbb05..b732e95e72 100644
--- a/repos/os/src/server/black_hole/main.cc
+++ b/repos/os/src/server/black_hole/main.cc
@@ -26,6 +26,7 @@
#include "capture.h"
#include "event.h"
#include "nic.h"
+#include "uplink.h"
/***************
@@ -45,6 +46,7 @@ struct Black_hole::Main
Genode::Constructible capture_root { };
Genode::Constructible event_root { };
Genode::Constructible nic_root { };
+ Genode::Constructible uplink_root { };
Main(Genode::Env &env) : env(env)
{
@@ -73,6 +75,11 @@ struct Black_hole::Main
nic_root.construct(env, heap);
env.parent().announce(env.ep().manage(*nic_root));
}
+
+ if (_config_rom.xml().has_sub_node("uplink")) {
+ uplink_root.construct(env, heap);
+ env.parent().announce(env.ep().manage(*uplink_root));
+ }
}
};
diff --git a/repos/os/src/server/black_hole/uplink.h b/repos/os/src/server/black_hole/uplink.h
new file mode 100644
index 0000000000..6cb65d6548
--- /dev/null
+++ b/repos/os/src/server/black_hole/uplink.h
@@ -0,0 +1,171 @@
+/*
+ * \brief Uplink session component and root
+ * \author Martin Stein
+ * \date 2022-02-12
+ */
+
+/*
+ * Copyright (C) 2022 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.
+ */
+
+#ifndef _UPLINK_H_
+#define _UPLINK_H_
+
+/* base includes */
+#include
+#include
+
+/* os includes */
+#include
+#include
+
+namespace Black_hole {
+
+ using namespace Genode;
+
+ class Uplink_session_base;
+ class Uplink_session;
+ class Uplink_root;
+}
+
+
+class Black_hole::Uplink_session_base
+{
+ friend class Uplink_session;
+
+ private:
+
+ class Buffer
+ {
+ private:
+
+ Ram_allocator &_ram_alloc;
+ Ram_dataspace_capability _ram_ds;
+
+ public:
+
+ Buffer(Ram_allocator &ram_alloc,
+ size_t const size)
+ :
+ _ram_alloc { ram_alloc },
+ _ram_ds { ram_alloc.alloc(size) }
+ { }
+
+ ~Buffer() { _ram_alloc.free(_ram_ds); }
+
+ Dataspace_capability ds() const { return _ram_ds; }
+ };
+
+ Env &_env;
+ Allocator &_alloc;
+ Nic::Packet_allocator _packet_alloc;
+ Buffer _tx_buf;
+ Buffer _rx_buf;
+
+ public:
+
+ Uplink_session_base(Env &env,
+ size_t tx_buf_size,
+ size_t rx_buf_size,
+ Allocator &alloc)
+ :
+ _env { env },
+ _alloc { alloc },
+ _packet_alloc { &_alloc },
+ _tx_buf { _env.ram(), tx_buf_size },
+ _rx_buf { _env.ram(), rx_buf_size }
+ { }
+};
+
+
+class Black_hole::Uplink_session : private Uplink_session_base,
+ public Uplink::Session_rpc_object
+{
+ private:
+
+ Signal_handler _packet_stream_handler;
+
+ void _handle_packet_stream()
+ {
+ while (_tx.sink()->packet_avail()) {
+
+ if (!_tx.sink()->ready_to_ack()) {
+ return;
+ }
+ Packet_descriptor const pkt { _tx.sink()->get_packet() };
+ if (!pkt.size() || !_tx.sink()->packet_valid(pkt)) {
+ continue;
+ }
+ _tx.sink()->acknowledge_packet(pkt);
+ }
+ }
+
+ public:
+
+ Uplink_session(Env &env,
+ size_t tx_buf_size,
+ size_t rx_buf_size,
+ Allocator &alloc)
+ :
+ Uplink_session_base { env, tx_buf_size,rx_buf_size, alloc },
+ Session_rpc_object {
+ _env.rm(), _tx_buf.ds(), _rx_buf.ds() ,&_packet_alloc,
+ _env.ep().rpc_ep() },
+
+ _packet_stream_handler {
+ env.ep(), *this, &Uplink_session::_handle_packet_stream }
+ {
+ _tx.sigh_ready_to_ack (_packet_stream_handler);
+ _tx.sigh_packet_avail (_packet_stream_handler);
+ _rx.sigh_ack_avail (_packet_stream_handler);
+ _rx.sigh_ready_to_submit(_packet_stream_handler);
+ }
+};
+
+
+class Black_hole::Uplink_root : public Root_component
+{
+ private:
+
+ Env &_env;
+
+ Uplink_session *_create_session(char const *args) override
+ {
+ 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, (size_t)sizeof(Uplink_session));
+ if (ram_quota < session_size)
+ throw Insufficient_ram_quota();
+
+ /*
+ * Check if donated ram quota suffices for both communication
+ * buffers and check for overflow
+ */
+ if (tx_buf_size + rx_buf_size < tx_buf_size ||
+ tx_buf_size + rx_buf_size > ram_quota - session_size) {
+ error("insufficient 'ram_quota', got ", ram_quota, ", "
+ "need ", tx_buf_size + rx_buf_size + session_size);
+ throw Insufficient_ram_quota();
+ }
+
+ return new (md_alloc())
+ Uplink_session(_env, tx_buf_size, rx_buf_size, *md_alloc());
+ }
+
+ public:
+
+ Uplink_root(Env &env,
+ Allocator &alloc)
+ :
+ Root_component { &env.ep().rpc_ep(), &alloc },
+ _env { env }
+ { }
+};
+
+#endif /* _UPLINK_H_ */
diff --git a/repos/os/src/test/black_hole/main.cc b/repos/os/src/test/black_hole/main.cc
index 0e73b4c0a9..e20fe9714c 100644
--- a/repos/os/src/test/black_hole/main.cc
+++ b/repos/os/src/test/black_hole/main.cc
@@ -14,12 +14,14 @@
/* base includes */
#include
#include
+#include
/* os includes */
#include
#include
#include
#include
+#include
#include
#include
#include
@@ -40,30 +42,99 @@ class Test::Main
enum {
NIC_BUF_SIZE = Nic::Packet_allocator::DEFAULT_PACKET_SIZE * 128,
NIC_PKT_SIZE = 100,
+ UPLINK_BUF_SIZE = Nic::Packet_allocator::DEFAULT_PACKET_SIZE * 128,
+ UPLINK_PKT_SIZE = 100,
};
- Env &_env;
- Heap _heap { _env.ram(), _env.rm() };
- Event::Connection _event { _env };
- Capture::Connection _capture { _env };
- Capture::Area _capture_screen_size { 1, 1 };
- Capture::Pixel _capture_pixels[1];
- Surface _capture_surface { _capture_pixels, _capture_screen_size };
- Capture::Connection::Screen _capture_screen { _capture, _env.rm(), _capture_screen_size };
- Audio_in::Connection _audio_in { _env, "left" };
- Audio_out::Connection _audio_out { _env, "left" };
- Allocator_avl _nic_tx_blk_alloc { &_heap };
- Nic::Connection _nic { _env, &_nic_tx_blk_alloc, NIC_BUF_SIZE, NIC_BUF_SIZE };
- Signal_handler _nic_handler { _env.ep(), *this, &Main::_handle_nic };
- unsigned long _nic_pkt_count { 0 };
+ Env &_env;
+ Heap _heap { _env.ram(), _env.rm() };
+ Event::Connection _event { _env };
+ Capture::Connection _capture { _env };
+ Capture::Area _capture_screen_size { 1, 1 };
+ Capture::Pixel _capture_pixels[1];
+ Surface _capture_surface { _capture_pixels, _capture_screen_size };
+ Capture::Connection::Screen _capture_screen { _capture, _env.rm(), _capture_screen_size };
+ Audio_in::Connection _audio_in { _env, "left" };
+ Audio_out::Connection _audio_out { _env, "left" };
+ Allocator_avl _nic_tx_blk_alloc { &_heap };
+ Nic::Connection _nic { _env, &_nic_tx_blk_alloc, NIC_BUF_SIZE, NIC_BUF_SIZE };
+ Signal_handler _nic_handler { _env.ep(), *this, &Main::_handle_nic };
+ unsigned long _nic_pkt_count { 0 };
+ Allocator_avl _uplink_tx_blk_alloc { &_heap };
+ Constructible _uplink { };
+ Signal_handler _uplink_handler { _env.ep(), *this, &Main::_handle_uplink };
+ unsigned long _uplink_pkt_count { 0 };
void _check_if_test_has_finished()
{
- if (_nic_pkt_count > 100) {
+ if (_nic_pkt_count > 100 &&
+ _uplink_pkt_count > 100) {
+
log("Finished");
}
}
+ void _reconstruct_uplink()
+ {
+ _uplink.destruct();
+ _uplink.construct(
+ _env, &_uplink_tx_blk_alloc, UPLINK_BUF_SIZE,
+ UPLINK_BUF_SIZE, Net::Mac_address { 2 });
+
+ _uplink->tx_channel()->sigh_ready_to_submit(_uplink_handler);
+ _uplink->tx_channel()->sigh_ack_avail(_uplink_handler);
+ _uplink->rx_channel()->sigh_ready_to_ack(_uplink_handler);
+ _uplink->rx_channel()->sigh_packet_avail(_uplink_handler);
+ }
+
+ void _handle_uplink()
+ {
+ if (!_uplink.constructed()) {
+ return;
+ }
+ if (_uplink->rx()->packet_avail()) {
+ class Uplink_rx_packet_avail { };
+ throw Uplink_rx_packet_avail { };
+ }
+ if (!_uplink->rx()->ack_slots_free()) {
+ class Uplink_no_rx_ack_slots_free { };
+ throw Uplink_no_rx_ack_slots_free { };
+ }
+ while (_uplink->tx()->ack_avail()) {
+
+ Packet_descriptor const pkt { _uplink->tx()->get_acked_packet() };
+ if (pkt.size() != UPLINK_PKT_SIZE) {
+ class Uplink_packet_size_unexpected { };
+ throw Uplink_packet_size_unexpected { };
+ }
+ _uplink->tx()->release_packet(pkt);
+ _uplink_pkt_count++;
+ }
+ _submit_uplink_pkts();
+ _check_if_test_has_finished();
+ }
+
+ void _submit_uplink_pkts()
+ {
+ for (unsigned idx { 0 }; idx < 40; idx++) {
+
+ if (idx == 10) {
+ _reconstruct_uplink();
+ }
+ if (!_uplink->tx()->ready_to_submit()) {
+ class Uplink_submit_queue_full { };
+ throw Uplink_submit_queue_full { };
+ }
+ Packet_descriptor pkt;
+ try { pkt = _uplink->tx()->alloc_packet(UPLINK_PKT_SIZE); }
+ catch (...) {
+ class Uplink_packet_alloc_failed { };
+ throw Uplink_packet_alloc_failed { };
+ }
+ _uplink->tx()->submit_packet(pkt);
+ }
+ }
+
void _handle_nic()
{
if (_nic.rx()->packet_avail()) {
@@ -110,6 +181,10 @@ class Test::Main
Main(Env &env) : _env { env }
{
+ /* test-drive uplink connection */
+ _reconstruct_uplink();
+ _submit_uplink_pkts();
+
/* test-drive nic connection */
_nic.tx_channel()->sigh_ready_to_submit(_nic_handler);
_nic.tx_channel()->sigh_ack_avail(_nic_handler);