genode/repos/os/src/test/packet_stream/main.cc

334 lines
7.9 KiB
C++

/*
* \brief Test for the packet-streaming interface
* \author Norman Feske
* \date 2009-11-11
*/
/*
* Copyright (C) 2009-2013 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/printf.h>
#include <base/env.h>
#include <base/thread.h>
#include <base/allocator_avl.h>
#include <timer_session/connection.h>
#include <os/packet_stream.h>
/**
* Specialized policy, using a small queue sizes
*
* Note that the ack queue size is smaller than the submit queue
* size.
*/
typedef Genode::Packet_stream_policy<Genode::Packet_descriptor, 8, 4, char>
Test_packet_stream_policy;
enum { STACK_SIZE = 4096 };
void Genode::Packet_stream_base::_debug_print_buffers()
{
Genode::printf("_ds_local_base = 0x%p\n", _ds_local_base);
Genode::printf("_submit_queue_offset = 0x%lx\n", _submit_queue_offset);
Genode::printf("_ack_queue_offset = 0x%lx\n", _ack_queue_offset);
Genode::printf("_bulk_buffer_offset = 0x%lx\n", _bulk_buffer_offset);
Genode::printf("_bulk_buffer_size = 0x%zx (%zd)\n", _bulk_buffer_size, _bulk_buffer_size);
}
/**
* Thread generating packets
*/
class Source : private Genode::Thread<STACK_SIZE>,
private Genode::Allocator_avl,
public Genode::Packet_stream_source<Test_packet_stream_policy>
{
private:
enum Operation { OP_NONE, OP_GENERATE, OP_ACKNOWLEDGE };
Operation _operation; /* current mode of operation */
Genode::Lock _lock; /* lock used as barrier in the thread loop */
unsigned _cnt; /* number of packets to produce */
void _generate_packets(unsigned cnt)
{
for (unsigned i = 0; i < cnt; i++) {
try {
enum { PACKET_SIZE = 1024 };
Packet_descriptor packet = alloc_packet(PACKET_SIZE);
char *content = packet_content(packet);
if (!content) {
PWRN("Source: invalid packet");
}
Genode::printf("Source: allocated packet (offset=0x%lx, size=0x%zd\n",
packet.offset(), packet.size());
for (unsigned i = 0; i < packet.size(); i++)
content[i] = i;
bool is_blocking = !ready_to_submit();
if (is_blocking)
Genode::printf("Source: submit queue is full, going to block\n");
submit_packet(packet);
if (is_blocking)
Genode::printf("Source: returned from submit_packet function\n");
} catch (Packet_stream_source<>::Packet_alloc_failed) {
PDBG("Source: Packet allocation failed");
}
}
}
void _acknowledge_packets(unsigned cnt)
{
for (unsigned i = 0; i < cnt; i++) {
if (!ack_avail())
Genode::printf("Source: acknowledgement queue is empty, going to block\n");
Packet_descriptor packet = get_acked_packet();
char *content = packet_content(packet);
if (!content) {
PWRN("Source: invalid packet");
} else {
/* validate packet content */
for (unsigned i = 0; i < packet.size(); i++) {
if (content[i] != (char)i) {
PERR("Source: packet content is corrupted\n");
break;
}
}
}
Genode::printf("Source: release packet (offset=0x%lx, size=0x%zd\n",
packet.offset(), packet.size());
release_packet(packet);
}
}
void entry()
{
for (;;) {
/* wait for a new 'generate' or 'acknowledge' call */
_lock.lock();
if (_operation == OP_GENERATE)
_generate_packets(_cnt);
if (_operation == OP_ACKNOWLEDGE)
_acknowledge_packets(_cnt);
}
}
public:
/**
* Constructor
*/
Source(Genode::Dataspace_capability ds_cap)
:
/* init bulk buffer allocator, storing its meta data on the heap */
Thread("source"),
Genode::Allocator_avl(Genode::env()->heap()),
Packet_stream_source<Test_packet_stream_policy>(this, ds_cap),
_operation(OP_NONE),
_lock(Genode::Lock::LOCKED),
_cnt(0)
{
Genode::printf("Source: packet stream buffers:");
debug_print_buffers();
start();
}
void generate(unsigned cnt)
{
_cnt = cnt;
_operation = OP_GENERATE;
_lock.unlock();
}
void acknowledge(unsigned cnt)
{
_cnt = cnt;
_operation = OP_ACKNOWLEDGE;
_lock.unlock();
}
};
class Sink : private Genode::Thread<STACK_SIZE>,
public Genode::Packet_stream_sink<Test_packet_stream_policy>
{
private:
enum Operation { OP_NONE, OP_PROCESS };
Operation _operation; /* current mode of operation */
Genode::Lock _lock; /* lock used as barrier in the thread loop */
unsigned _cnt; /* number of packets to produce */
void _process_packets(unsigned cnt)
{
for (unsigned i = 0; i < cnt; i++) {
if (!packet_avail())
Genode::printf("Sink: no packet available, going to block\n");
Packet_descriptor packet = get_packet();
char *content = packet_content(packet);
if (!content)
PWRN("Invalid packet");
Genode::printf("Sink: got packet (offset=0x%lx, size=0x%zd\n",
packet.offset(), packet.size());
if (!ready_to_ack())
Genode::printf("Sink: ack queue is full, going to block\n");
acknowledge_packet(packet);
}
}
void entry()
{
for (;;) {
/* wait for a new 'process' call */
_lock.lock();
if (_operation == OP_PROCESS)
_process_packets(_cnt);
}
}
public:
/**
* Constructor
*/
Sink(Genode::Dataspace_capability ds_cap)
:
Thread("sink"),
Packet_stream_sink<Test_packet_stream_policy>(ds_cap),
_operation(OP_NONE),
_lock(Genode::Lock::LOCKED),
_cnt(0)
{
Genode::printf("Sink: packet stream buffers:");
debug_print_buffers();
start();
}
void process(unsigned cnt)
{
_cnt = cnt;
_operation = OP_PROCESS;
_lock.unlock();
}
};
void test_1_good_case(Timer::Session *timer, Source *source, Sink *sink,
unsigned batch_size, unsigned rounds)
{
for (unsigned i = 0; i < rounds; i++) {
enum { DELAY = 200 };
Genode::printf("- round %u -", i);
Genode::printf("generate %u packets, fitting in bulk buffer and submit queue\n", batch_size);
source->generate(batch_size);
timer->msleep(DELAY);
Genode::printf("process %u packets\n", batch_size);
sink->process(batch_size);
timer->msleep(DELAY);
Genode::printf("acknowledge %u packets\n", batch_size);
source->acknowledge(batch_size);
timer->msleep(DELAY);
}
}
void test_2_flood_submit(Timer::Session *timer, Source *source, Sink *sink)
{
enum { PACKETS = 9 }; /* more than the number of submit queue entries */
enum { DELAY = 200 };
source->generate(PACKETS);
timer->msleep(DELAY);
Genode::printf("- source should block, process 3 packets, source should wake up -\n");
sink->process(1);
timer->msleep(5*DELAY);
sink->process(2);
Genode::printf("- let source acknowledge %u packets -\n", 3);
source->acknowledge(3);
timer->msleep(DELAY);
Genode::printf("- process and acknowledge the remaining packets in batches 3 -\n");
for (int i = 0; i < 2; i++) {
sink->process(3);
timer->msleep(DELAY);
source->acknowledge(3);
timer->msleep(DELAY);
}
}
using namespace Genode;
int main(int, char **)
{
printf("--- packet stream test ---\n");
Timer::Connection timer;
enum { TRANSPORT_DS_SIZE = 16*1024 };
Dataspace_capability ds_cap = env()->ram_session()->alloc(TRANSPORT_DS_SIZE);
Source source(ds_cap);
Sink sink(ds_cap);
/* wire data-flow signals beteen source and sink */
source.register_sigh_packet_avail(sink.sigh_packet_avail());
source.register_sigh_ready_to_ack(sink.sigh_ready_to_ack());
sink.register_sigh_ready_to_submit(source.sigh_ready_to_submit());
sink.register_sigh_ack_avail(source.sigh_ack_avail());
timer.msleep(1000);
printf("\n-- test 1: good case, no queue pressure, no blocking --\n");
test_1_good_case(&timer, &source, &sink, 3, 5);
printf("\n-- test 2: flood submit queue, sender blocks, gets woken up --\n");
test_2_flood_submit(&timer, &source, &sink);
printf("waiting to settle down\n");
timer.msleep(2*1000);
printf("--- end of packet stream test ---\n");
return 0;
}