mirror of
https://github.com/genodelabs/genode.git
synced 2025-02-21 02:01:38 +00:00
parent
7a0bcbbccb
commit
a9eecc1a2d
@ -13,26 +13,24 @@
|
||||
* under the terms of the GNU General Public License version 2.
|
||||
*/
|
||||
|
||||
#include <base/env.h>
|
||||
#include <base/sleep.h>
|
||||
#include <os/server.h>
|
||||
#include <base/component.h>
|
||||
#include <base/heap.h>
|
||||
#include <root/component.h>
|
||||
#include <util/arg_string.h>
|
||||
#include <util/misc_math.h>
|
||||
#include <nic/component.h>
|
||||
#include <nic/packet_allocator.h>
|
||||
|
||||
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<Loopback_component>
|
||||
class Nic_loopback::Root : public Root_component<Session_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<Loopback_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<Loopback_component>(&ep.rpc_ep(), &md_alloc),
|
||||
_ep(ep)
|
||||
Root_component<Session_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);
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,3 @@
|
||||
TARGET = nic_loopback
|
||||
SRC_CC = main.cc
|
||||
LIBS = base server
|
||||
LIBS = base
|
||||
|
@ -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 <base/component.h>
|
||||
#include <base/log.h>
|
||||
#include <base/heap.h>
|
||||
#include <base/allocator_avl.h>
|
||||
#include <nic_session/connection.h>
|
||||
#include <nic/packet_allocator.h>
|
||||
#include <timer_session/connection.h>
|
||||
|
||||
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<Base> _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 <typename... ARGS>
|
||||
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> _roundtrip;
|
||||
Constructible<Batch> _batch;
|
||||
|
||||
Signal_handler<Main> _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); }
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user