Replace Nic driver interface by customizable component

Removed the Nic::Driver implementation. All nic servers now inherit from
Nic::Session_component. Packet stream signals are dispatched to
the 'handle_packet_stream' function within a session component. Thus, nic
servers now have direct access to the packet stream channels, making handling
more flexible.

Adjusted nic_loobpack, dde_ipxe, wifi, usb, lan9118, Linux nic, and OpenVPN to
the new interface.

Fixes #1602
This commit is contained in:
Sebastian Sumpf 2015-06-23 13:13:20 +02:00 committed by Christian Helmuth
parent cc4febd1c2
commit 463c9bec17
20 changed files with 1125 additions and 1832 deletions

View File

@ -1,6 +1,7 @@
/*
* \brief NIC driver based on iPXE
* \author Christian Helmuth
* \author Sebastian Sumpf
* \date 2011-11-17
*/
@ -17,131 +18,133 @@
#include <base/printf.h>
#include <cap_session/connection.h>
#include <nic/component.h>
#include <nic/root.h>
#include <os/server.h>
#include <dde_ipxe/nic.h>
namespace Ipxe {
class Ipxe_session_component : public Nic::Session_component
{
public:
class Driver : public Nic::Driver
{
public:
static Ipxe_session_component *instance;
static Driver *instance;
private:
private:
Nic::Mac_address _mac_addr;
Server::Entrypoint &_ep;
static void _rx_callback(unsigned if_index,
const char *packet,
unsigned packet_len)
{
if (instance)
instance->_receive(packet, packet_len);
}
Nic::Mac_address _mac_addr;
Nic::Rx_buffer_alloc &_alloc;
Nic::Driver_notification &_notify;
static void _link_callback()
{
if (instance)
instance->_link_state_changed();
}
static void _rx_callback(unsigned if_index,
const char *packet,
unsigned packet_len)
{
instance->rx_handler(packet, packet_len);
bool _send()
{
using namespace Genode;
if (!_tx.sink()->ready_to_ack())
return false;
if (!_tx.sink()->packet_avail())
return false;
Packet_descriptor packet = _tx.sink()->get_packet();
if (!packet.valid()) {
PWRN("Invalid tx packet");
return true;
}
static void _link_callback() { instance->link_state_changed(); }
if (dde_ipxe_nic_tx(1, _tx.sink()->packet_content(packet), packet.size()))
PWRN("Sending packet failed!");
public:
_tx.sink()->acknowledge_packet(packet);
return true;
}
Driver(Server::Entrypoint &ep, Nic::Rx_buffer_alloc &alloc,
Nic::Driver_notification &notify)
: _ep(ep), _alloc(alloc), _notify(notify)
{
PINF("--- init callbacks");
dde_ipxe_nic_register_callbacks(_rx_callback, _link_callback);
void _receive(const char *packet, unsigned packet_len)
{
_handle_packet_stream();
dde_ipxe_nic_get_mac_addr(1, _mac_addr.addr);
PINF("--- get MAC address %02x:%02x:%02x:%02x:%02x:%02x",
_mac_addr.addr[0] & 0xff, _mac_addr.addr[1] & 0xff,
_mac_addr.addr[2] & 0xff, _mac_addr.addr[3] & 0xff,
_mac_addr.addr[4] & 0xff, _mac_addr.addr[5] & 0xff);
if (!_rx.source()->ready_to_submit())
return;
try {
Nic::Packet_descriptor p = _rx.source()->alloc_packet(packet_len);
Genode::memcpy(_rx.source()->packet_content(p), packet, packet_len);
_rx.source()->submit_packet(p);
} catch (...) {
PDBG("failed to process received packet");
}
}
~Driver() { dde_ipxe_nic_unregister_callbacks(); }
void _handle_packet_stream() override
{
while (_rx.source()->ack_avail())
_rx.source()->release_packet(_rx.source()->get_acked_packet());
void rx_handler(const char *packet, unsigned packet_len)
{
try {
void *buffer = _alloc.alloc(packet_len);
Genode::memcpy(buffer, packet, packet_len);
_alloc.submit();
} catch (...) {
PDBG("failed to process received packet");
}
}
while (_send()) ;
}
void link_state_changed() { _notify.link_state_changed(); }
public:
Ipxe_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)
: Session_component(tx_buf_size, rx_buf_size, rx_block_md_alloc, ram_session, ep)
{
instance = this;
PINF("--- init callbacks");
dde_ipxe_nic_register_callbacks(_rx_callback, _link_callback);
dde_ipxe_nic_get_mac_addr(1, _mac_addr.addr);
PINF("--- get MAC address %02x:%02x:%02x:%02x:%02x:%02x",
_mac_addr.addr[0] & 0xff, _mac_addr.addr[1] & 0xff,
_mac_addr.addr[2] & 0xff, _mac_addr.addr[3] & 0xff,
_mac_addr.addr[4] & 0xff, _mac_addr.addr[5] & 0xff);
}
~Ipxe_session_component()
{
instance = nullptr;
dde_ipxe_nic_unregister_callbacks();
}
/**************************************
** Nic::Session_component interface **
**************************************/
Nic::Mac_address mac_address() override { return _mac_addr; }
bool link_state() override
{
return dde_ipxe_nic_link_state(1);
}
};
/***************************
** Nic::Driver interface **
***************************/
Nic::Mac_address mac_address() override { return _mac_addr; }
bool link_state() override
{
return dde_ipxe_nic_link_state(1);
}
void tx(char const *packet, Genode::size_t size)
{
if (dde_ipxe_nic_tx(1, packet, size))
PWRN("Sending packet failed!");
}
/******************************
** Irq_activation interface **
******************************/
void handle_irq(int) { /* not used */ }
};
} /* namespace Ipxe */
Ipxe::Driver * Ipxe::Driver::instance = 0;
Ipxe_session_component *Ipxe_session_component::instance;
struct Main
{
Server::Entrypoint &ep;
Genode::Sliced_heap sliced_heap;
Server::Entrypoint &ep;
struct Factory : public Nic::Driver_factory
{
Server::Entrypoint &ep;
Nic::Root<Ipxe_session_component> root {ep, *Genode::env()->heap() };
Factory(Server::Entrypoint &ep) : ep(ep) { }
Nic::Driver *create(Nic::Rx_buffer_alloc &alloc,
Nic::Driver_notification &notify)
{
Ipxe::Driver::instance = new (Genode::env()->heap()) Ipxe::Driver(ep, alloc, notify);
return Ipxe::Driver::instance;
}
void destroy(Nic::Driver *)
{
Genode::destroy(Genode::env()->heap(), Ipxe::Driver::instance);
Ipxe::Driver::instance = 0;
}
} factory;
Nic::Root root;
Main(Server::Entrypoint &ep)
:
ep(ep),
sliced_heap(Genode::env()->ram_session(), Genode::env()->rm_session()),
factory(ep),
root(&ep.rpc_ep(), &sliced_heap, factory)
Main(Server::Entrypoint &ep) : ep(ep)
{
PINF("--- iPXE NIC driver started ---\n");

View File

@ -1,181 +0,0 @@
/*
* \brief NIC driver based on iPXE for performance measurements solely
* \author Christian Helmuth
* \date 2011-11-17
*/
/*
* Copyright (C) 2011-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.
*/
/* Genode include */
#include <base/env.h>
#include <base/sleep.h>
#include <base/printf.h>
#include <cap_session/connection.h>
#include <nic/component.h>
#include <os/server.h>
#include <nic/stat.h>
#include <nic/packet_allocator.h>
#include <nic_session/connection.h>
/* DDE */
extern "C" {
#include <dde_ipxe/nic.h>
}
namespace Ipxe {
class Driver : public Nic::Driver
{
public:
static Driver *instance;
private:
Nic::Mac_address _mac_addr;
Nic::Rx_buffer_alloc &_alloc;
Nic::Driver_notification &_notify;
Timer::Connection _timer;
Nic::Measurement _stat;
static void _rx_callback(unsigned if_index,
const char *packet,
unsigned packet_len)
{
instance->rx_handler_stat(packet, packet_len);
}
static void _link_callback() { instance->link_state_changed(); }
public:
Driver(Server::Entrypoint &ep, Nic::Rx_buffer_alloc &alloc,
Nic::Driver_notification &notify)
: _alloc(alloc), _notify(notify), _stat(_timer)
{
PINF("--- init iPXE NIC");
int cnt = dde_ipxe_nic_init(&ep);
PINF(" number of devices: %d", cnt);
PINF("--- init callbacks");
dde_ipxe_nic_register_callbacks(_rx_callback, _link_callback);
dde_ipxe_nic_get_mac_addr(1, _mac_addr.addr);
PINF("--- get MAC address %02x:%02x:%02x:%02x:%02x:%02x",
_mac_addr.addr[0] & 0xff, _mac_addr.addr[1] & 0xff,
_mac_addr.addr[2] & 0xff, _mac_addr.addr[3] & 0xff,
_mac_addr.addr[4] & 0xff, _mac_addr.addr[5] & 0xff);
_stat.set_mac(_mac_addr.addr);
}
void rx_handler_stat(const char *packet, unsigned packet_len)
{
Genode::addr_t test = reinterpret_cast<Genode::addr_t>(packet);
void * buffer = reinterpret_cast<void *>(test);
Net::Ethernet_frame *eth =
new (buffer) Net::Ethernet_frame(packet_len);
_stat.data(eth, packet_len);
}
void rx_handler(const char *packet, unsigned packet_len)
{
void *buffer = _alloc.alloc(packet_len);
Genode::memcpy(buffer, packet, packet_len);
_alloc.submit();
}
void link_state_changed() { _notify.link_state_changed(); }
/***************************
** Nic::Driver interface **
***************************/
Nic::Mac_address mac_address() { return _mac_addr; }
bool link_state() override
{
return dde_ipxe_nic_link_state(1);
}
void tx(char const *packet, Genode::size_t size)
{
if (dde_ipxe_nic_tx(1, packet, size))
PWRN("Sending packet failed!");
}
/******************************
** Irq_activation interface **
******************************/
void handle_irq(int) { /* not used */ }
};
} /* namespace Ipxe */
Ipxe::Driver * Ipxe::Driver::instance = 0;
struct Main
{
Server::Entrypoint &ep;
Genode::Sliced_heap sliced_heap;
struct Factory : public Nic::Driver_factory
{
Server::Entrypoint &ep;
Factory(Server::Entrypoint &ep) : ep(ep) { }
Nic::Driver *create(Nic::Rx_buffer_alloc &alloc,
Nic::Driver_notification &notify)
{
Ipxe::Driver::instance = new (Genode::env()->heap()) Ipxe::Driver(ep, alloc, notify);
return Ipxe::Driver::instance;
}
void destroy(Nic::Driver *)
{
Genode::destroy(Genode::env()->heap(), Ipxe::Driver::instance);
Ipxe::Driver::instance = 0;
}
} factory;
Nic::Root root;
Main(Server::Entrypoint &ep)
:
ep(ep),
sliced_heap(Genode::env()->ram_session(), Genode::env()->rm_session()),
factory(ep),
root(&ep.rpc_ep(), &sliced_heap, factory)
{
PINF("--- iPXE NIC driver started ---\n");
Genode::env()->parent()->announce(ep.manage(root));
root.session("ram_quota=155648, tx_buf_size=65536, rx_buf_size=65536",
Genode::Affinity());
}
};
/************
** Server **
************/
namespace Server {
char const *name() { return "nic_drv_stat_ep"; }
size_t stack_size() { return 2*1024*sizeof(long); }
void construct(Entrypoint &ep) { static Main server(ep); }
}

View File

@ -1,3 +0,0 @@
TARGET = nic_drv_stat
LIBS = base server dde_ipxe_nic net-stat
SRC_CC = main.cc

View File

@ -1,327 +0,0 @@
/*
* \brief Block-session implementation for network devices
* \author Sebastian Sumpf
* \date 2012-07-05
*/
/*
* Copyright (C) 2012-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.
*/
#ifndef _NIC__COMPONENT_H_
#define _NIC__COMPONENT_H_
#include <root/component.h>
#include <nic/packet_allocator.h>
#include <nic_session/rpc_object.h>
#include <timer_session/connection.h>
#include <signal/dispatch.h>
#define BENCH 0
namespace Nic {
using namespace Genode;
class Session_component;
#if BENCH
struct Counter : public Genode::Thread<8192>
{
char const *prefix;
int cnt;
int burst;
size_t size;
void entry()
{
Timer::Connection _timer;
int interval = 5;
while(1) {
_timer.msleep(interval * 1000);
PDBG("%s: Packets %d/s (in %d burst packets) bytes/s: %d",
prefix, cnt / interval, burst / interval, size / interval);
cnt = 0;
size = 0;
burst = 0;
}
}
void inc(size_t s) { cnt++; size += s; }
void inc_burst() { burst++; }
Counter(char const *prefix)
: Thread("counter"), prefix(prefix), cnt(0), burst(0), size(0)
{ start(); }
};
#else
struct Counter
{
Counter(char const *) { };
void inc(size_t) { }
void inc_burst() { }
};
#endif
struct Device : ::Device
{
Session_component *_session;
/**
* Transmit data to driver
*/
virtual bool tx(addr_t virt, size_t size) = 0;
/**
* Return mac address of device
*/
virtual Mac_address mac_address() = 0;
/**
* Return current link-state (true if link detected)
*/
virtual bool link_state() = 0;
/**
* Set session belonging to this driver
*/
void session(Session_component *s) { _session = s; }
/**
* Check for session
*/
bool session() { return _session != 0; }
/**
* Alloc an SKB
*/
virtual sk_buff *alloc_skb() = 0;
/**
* Submit SKB to device
*/
virtual void tx_skb(sk_buff *skb) = 0;
/**
* Setup SKB with 'data' of 'size', return 'false' if SKB is longer than
* 'end'.
*/
virtual bool skb_fill(struct sk_buff *skb, unsigned char *data, Genode::size_t size, unsigned char *end) = 0;
/**
* Call driver fixup function on SKB
*/
virtual void tx_fixup(struct sk_buff *skb) = 0;
/**
* Return true if device supports burst operations
*/
virtual bool burst() = 0;
Device() : _session(0) { }
};
class Session_component : public Nic::Packet_allocator,
public Packet_session_component<Session_rpc_object>
{
private:
Device *_device; /* device this session is using */
Tx::Sink *_tx_sink; /* client packet sink */
bool _tx_alloc; /* get next packet from client or use _tx_packet */
Packet_descriptor _tx_packet; /* saved packet in case of driver errors */
Signal_context_capability _link_state_sigh;
void _send_packet_avail_signal() {
Signal_transmitter(_tx.sigh_packet_avail()).submit(); }
protected:
void _process_packets(unsigned)
{
static sk_buff work_skb; /* dummy skb for fixup calls */
static Counter counter("TX");
int tx_cnt = 0;
unsigned size = 0;
sk_buff *skb = 0;
unsigned char *ptr = 0;
/* submit received packets to lower layer */
while (_tx_sink->packet_avail())
{
Packet_descriptor packet = _tx_alloc ? _tx_sink->get_packet() : _tx_packet;
addr_t virt = (addr_t)_tx_sink->packet_content(packet);
if (_device->burst()) {
if (!ptr || !_device->skb_fill(&work_skb, ptr, packet.size(), skb->end)) {
/* submit batch to device */
if (ptr) {
_device->tx_skb(skb);
tx_cnt++;
counter.inc_burst();
}
/* alloc new SKB */
skb = _device->alloc_skb();
if (!skb) {
_send_packet_avail_signal();
_tx_alloc = false;
_tx_packet = packet;
return;
}
_tx_alloc = true;
ptr = skb->data;
work_skb.data = 0;
_device->skb_fill(&work_skb, ptr, packet.size(), skb->end);
}
/* copy packet to current data pos */
Genode::memcpy(work_skb.data, (void *)virt, packet.size());
/* call fixup on dummy SKB */
_device->tx_fixup(&work_skb);
/* advance to next slot */
ptr = work_skb.end;
skb->len += work_skb.truesize;
} else {
/* send to driver */
if (!(_device->tx(virt, packet.size()))) {
_send_packet_avail_signal();
_tx_alloc = false;
_tx_packet = packet;
return;
}
_tx_alloc = true;
tx_cnt++;
}
counter.inc(packet.size());
/* acknowledge to client */
_tx_sink->acknowledge_packet(packet);
/* it's cooperative scheduling - be nice */
if (tx_cnt == 20)
break;
}
/* sumbit last skb */
if (skb) {
_device->tx_skb(skb);
counter.inc_burst();
}
/* for large TCP/s check RX immediately */
Irq::check_irq();
/* release acknowledged packets */
_rx_ack(false);
if (_tx_sink->packet_avail())
_send_packet_avail_signal();
}
void _rx_ack(bool block = true)
{
while (_rx.source()->ack_avail() || block)
{
Packet_descriptor packet = _rx.source()->get_acked_packet();
/* free packet buffer */
_rx.source()->release_packet(packet);
block = false;
}
}
public:
/**
* Constructor
*/
Session_component(Dataspace_capability tx_ds,
Dataspace_capability rx_ds,
Server::Entrypoint &ep,
::Device *device)
:
Nic::Packet_allocator(Genode::env()->heap()),
Packet_session_component(tx_ds, rx_ds, this, ep),
_device(static_cast<Device *>(device)),
_tx_sink(Session_rpc_object::_tx.sink()),
_tx_alloc(true)
{ _device->session(this); }
/**
* Link state changed (called from driver)
*/
void link_state_changed()
{
if (_link_state_sigh.valid())
Genode::Signal_transmitter(_link_state_sigh).submit();
}
Mac_address mac_address() { return _device->mac_address(); }
bool link_state() override {
return _device->link_state(); }
void link_state_sigh(Genode::Signal_context_capability sigh) {
_link_state_sigh = sigh; }
/**
* Send packet to client (called form driver)
*/
void rx(addr_t virt, size_t size)
{
static Counter counter("RX");
while (true) {
try {
Packet_descriptor p =_rx.source()->alloc_packet(size);
Genode::memcpy(_rx.source()->packet_content(p), (void*)virt, size);
_rx.source()->submit_packet(p);
counter.inc(size);
} catch (...) {
/* ack or block */
_rx_ack();
continue;
}
break;
}
_rx_ack(false);
}
};
/*
* Shortcut for single-client root component
*/
typedef Root_component<Session_component, Single_client> Root_component;
/**
* Root component, handling new session requests
*/
class Root : public Packet_root<Root_component, Session_component, CACHED>
{
public:
Root(Server::Entrypoint &ep, Allocator *md_alloc,
Device *device)
: Packet_root(ep, md_alloc, device) { }
};
}
#endif /* _NIC__COMPONENT_H_ */

View File

@ -1,133 +0,0 @@
/**
* \brief Packet-stream-session components
* \author Sebastian Sumpf
* \author Norman Feske
* \date 2012-07-06
*/
/*
* Copyright (C) 2012-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.
*/
#ifndef _SIGNAL__DISPATCHER_H_
#define _SIGNAL__DISPATCHER_H_
#include "signal.h"
#include "platform/lx_mem.h"
/**
* Session components that overrides signal handlers
*/
template <typename RPC>
class Packet_session_component : public RPC
{
private:
Genode::Signal_rpc_member<Packet_session_component> _dispatcher;
protected:
virtual void _process_packets(unsigned) = 0;
public:
Packet_session_component(Genode::Dataspace_capability tx_ds,
Server::Entrypoint &ep)
: RPC(tx_ds, ep.rpc_ep()),
_dispatcher(ep, *this, &Packet_session_component::_process_packets)
{
/*
* Register '_process_packets' dispatch function as signal
* handler for packet-avail and ready-to-ack signals.
*/
RPC::_tx.sigh_packet_avail(_dispatcher);
RPC::_tx.sigh_ready_to_ack(_dispatcher);
}
Packet_session_component(Genode::Dataspace_capability tx_ds,
Genode::Dataspace_capability rx_ds,
Genode::Range_allocator *rx_buffer_alloc,
Server::Entrypoint &ep)
: RPC(tx_ds, rx_ds, rx_buffer_alloc, ep.rpc_ep()),
_dispatcher(ep, *this, &Packet_session_component::_process_packets)
{
/*
* Register '_process_packets' dispatch function as signal
* handler for packet-avail and ready-to-ack signals.
*/
RPC::_tx.sigh_packet_avail(_dispatcher);
RPC::_tx.sigh_ready_to_ack(_dispatcher);
}
};
/**
* Abstract device
*/
struct Device { };
/**
* Root component, handling new session requests
*/
template <typename ROOT_COMPONENT, typename SESSION_COMPONENT,
Genode::Cache_attribute CACHEABILITY>
class Packet_root : public ROOT_COMPONENT
{
private:
Server::Entrypoint &_ep;
Device *_device;
protected:
/**
* Always returns the singleton block-session component
*/
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);
/* delete ram quota by the memory needed for the session */
size_t session_size = max((size_t)4096,
sizeof(SESSION_COMPONENT)
+ sizeof(Allocator_avl));
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) {
PERR("insufficient 'ram_quota', got %zd, need %zd",
ram_quota, tx_buf_size + session_size);
throw Root::Quota_exceeded();
}
return new (ROOT_COMPONENT::md_alloc())
SESSION_COMPONENT(Backend_memory::alloc(tx_buf_size, CACHEABILITY),
Backend_memory::alloc(rx_buf_size, CACHEABILITY),
_ep, _device);
}
public:
Packet_root(Server::Entrypoint &ep, Genode::Allocator *md_alloc,
Device *device)
: ROOT_COMPONENT(&ep.rpc_ep(), md_alloc),
_ep(ep), _device(device) { }
};
#endif /* _SIGNAL__DISPATCHER_H_ */

View File

@ -0,0 +1,272 @@
/*
* \brief Block-session implementation for network devices
* \author Sebastian Sumpf
* \date 2012-07-05
*/
/*
* Copyright (C) 2012-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.
*/
#ifndef _USB_NIC_COMPONENT_H_
#define _USB_NIC_COMPONENT_H_
#include <nic/component.h>
#include <root/component.h>
namespace Usb_nic {
using namespace Genode;
class Session_component;
struct Device;
};
struct Usb_nic::Device
{
Session_component *_session;
/**
* Transmit data to driver
*/
virtual bool tx(addr_t virt, size_t size) = 0;
/**
* Return mac address of device
*/
virtual Nic::Mac_address mac_address() = 0;
/**
* Return current link-state (true if link detected)
*/
virtual bool link_state() = 0;
/**
* Set session belonging to this driver
*/
void session(Session_component *s) { _session = s; }
/**
* Check for session
*/
bool session() { return _session != 0; }
/**
* Alloc an SKB
*/
virtual sk_buff *alloc_skb() = 0;
/**
* Submit SKB to device
*/
virtual void tx_skb(sk_buff *skb) = 0;
/**
* Setup SKB with 'data' of 'size', return 'false' if SKB is longer than
* 'end'.
*/
virtual bool skb_fill(struct sk_buff *skb, unsigned char *data, Genode::size_t size, unsigned char *end) = 0;
/**
* Call driver fixup function on SKB
*/
virtual void tx_fixup(struct sk_buff *skb) = 0;
/**
* Return true if device supports burst operations
*/
virtual bool burst() = 0;
Device() : _session(0) { }
};
class Usb_nic::Session_component : public Nic::Session_component
{
private:
Device *_device; /* device this session is using */
protected:
void _send_burst()
{
static sk_buff work_skb; /* dummy skb for fixup calls */
static Packet_descriptor save;
sk_buff *skb = nullptr;
unsigned char *ptr = nullptr;
/* submit received packets to lower layer */
while (((_tx.sink()->packet_avail() || save.valid()) && _tx.sink()->ready_to_ack()))
{
/* alloc skb */
if (!skb) {
if (!(skb = _device->alloc_skb()))
return;
ptr = skb->data;
work_skb.data = nullptr;
}
Packet_descriptor packet = save.valid() ? save : _tx.sink()->get_packet();
save = Packet_descriptor();
if (!_device->skb_fill(&work_skb, ptr, packet.size(), skb->end)) {
/* submit batch */
_device->tx_skb(skb);
skb = nullptr;
save = packet;
continue;
}
/* copy packet to current data pos */
Genode::memcpy(work_skb.data, _tx.sink()->packet_content(packet), packet.size());
/* call fixup on dummy SKB */
_device->tx_fixup(&work_skb);
/* advance to next slot */
ptr = work_skb.end;
skb->len += work_skb.truesize;
/* acknowledge to client */
_tx.sink()->acknowledge_packet(packet);
}
/* submit last skb */
if (skb)
_device->tx_skb(skb);
}
bool _send()
{
if (!_tx.sink()->ready_to_ack())
return false;
if (!_tx.sink()->packet_avail())
return false;
Genode::Packet_descriptor packet = _tx.sink()->get_packet();
if (!packet.valid()) {
PWRN("Invalid tx packet");
return true;
}
bool ret = _device->tx((addr_t)_tx.sink()->packet_content(packet), packet.size());
_tx.sink()->acknowledge_packet(packet);
return ret;
}
void _handle_packet_stream() override
{
while (_rx.source()->ack_avail())
_rx.source()->release_packet(_rx.source()->get_acked_packet());
if (_device->burst())
_send_burst();
else
while (_send());
}
public:
/**
* Constructor
*/
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,
Device *device)
: Nic::Session_component(tx_buf_size, rx_buf_size, rx_block_md_alloc, ram_session, ep),
_device(device)
{ _device->session(this); }
Nic::Mac_address mac_address() override { return _device->mac_address(); }
bool link_state() override { return _device->link_state(); }
void link_state_changed() { _link_state_changed(); }
/**
* Send packet to client (called from driver)
*/
void rx(addr_t virt, size_t size)
{
_handle_packet_stream();
if (!_rx.source()->ready_to_submit())
return;
try {
Packet_descriptor p =_rx.source()->alloc_packet(size);
Genode::memcpy(_rx.source()->packet_content(p), (void*)virt, size);
_rx.source()->submit_packet(p);
} catch (...) {
/* drop */
return;
}
}
};
/*
* Shortcut for single-client root component
*/
typedef Genode::Root_component<Usb_nic::Session_component, Genode::Single_client> Root_component;
/**
* Root component, handling new session requests
*/
class Root : public Root_component
{
private:
Server::Entrypoint &_ep;
Usb_nic::Device *_device;
protected:
Usb_nic::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(Usb_nic::Session_component));
if (ram_quota < session_size)
throw Genode::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 Genode::Root::Quota_exceeded();
}
return new (Root::md_alloc())
Usb_nic::Session_component(tx_buf_size, rx_buf_size,
*env()->heap(),
*env()->ram_session(),
_ep, _device);
}
public:
Root(Server::Entrypoint &ep, Genode::Allocator &md_alloc,
Usb_nic::Device *device)
: Root_component(&ep.rpc_ep(), &md_alloc),
_ep(ep), _device(device)
{ }
};
#endif /* _USB_NIC_COMPONENT_H_ */

View File

@ -25,7 +25,7 @@
#include <linux/usb/usbnet.h>
#include <extern_c_end.h>
#include <nic/component.h>
#include <usb_nic_component.h>
#include "signal.h"
@ -137,7 +137,7 @@ typedef struct sk_buff* (*fixup_t)(struct usbnet *, struct sk_buff *, gfp_t);
/**
* Net_device to session glue code
*/
class Nic_device : public Nic::Device
class Nic_device : public Usb_nic::Device
{
public:
@ -320,7 +320,7 @@ int register_netdev(struct net_device *ndev)
/* XXX: move to 'main' */
if (!announce) {
static Nic::Root root(_signal->ep(), env()->heap(), nic);
static ::Root root(_signal->ep(), *env()->heap(), nic);
announce = true;
@ -333,10 +333,6 @@ int register_netdev(struct net_device *ndev)
if (ndev->netdev_ops->ndo_set_rx_mode)
ndev->netdev_ops->ndo_set_rx_mode(ndev);
/*
if(ndev->netdev_ops->ndo_change_mtu)
ndev->netdev_ops->ndo_change_mtu(ndev, 4000);
*/
_nic = nic;
env()->parent()->announce(_signal->ep().rpc_ep().manage(&root));
}

View File

@ -1,214 +0,0 @@
/*
* \brief Nic-session implementation for network devices
* \author Sebastian Sumpf
* \date 2012-07-05
*/
/*
* Copyright (C) 2012-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.
*/
#ifndef _NIC__COMPONENT_H_
#define _NIC__COMPONENT_H_
/* Genode includes */
#include <nic/packet_allocator.h>
#include <nic_session/rpc_object.h>
#include <root/component.h>
#include <timer_session/connection.h>
/* local includes */
#include <nic/dispatch.h>
#include <extern_c_begin.h>
# include <linux/skbuff.h>
#include <extern_c_end.h>
namespace Nic {
using namespace Genode; /* FIXME */
struct Session_component;
struct Device;
typedef Genode::Root_component<Session_component, Single_client> Root_component;
struct Root;
}
struct Nic::Device
{
/**
* Transmit data to driver
*/
virtual bool tx(addr_t virt, size_t size) = 0;
/**
* Return mac address of device
*/
virtual Mac_address mac_address() = 0;
/**
* Return link state (true if link detected)
*/
virtual bool link_state() = 0;
/**
* Set session belonging to this driver
*/
virtual void session(Session_component *s) = 0;
Device() { }
virtual ~Device() { }
};
class Nic::Session_component : public Nic::Packet_allocator,
public Packet_session_component<Session_rpc_object>
{
private:
Device &_device; /* device this session is using */
Tx::Sink *_tx_sink; /* client packet sink */
bool _tx_alloc; /* get next packet from client or use _tx_packet */
Packet_descriptor _tx_packet; /* saved packet in case of driver errors */
Signal_context_capability _link_state_sigh;
void _send_packet_avail_signal() {
Signal_transmitter(_tx.sigh_packet_avail()).submit(); }
protected:
void _process_packets(unsigned)
{
int tx_cnt = 0;
/* submit received packets to lower layer */
while (_tx_sink->packet_avail()) {
Packet_descriptor packet = _tx_alloc ? _tx_sink->get_packet() : _tx_packet;
addr_t virt = (addr_t)_tx_sink->packet_content(packet);
/* send to driver */
if (!(_device.tx(virt, packet.size()))) {
_send_packet_avail_signal();
_tx_alloc = false;
_tx_packet = packet;
return;
}
_tx_alloc = true;
tx_cnt++;
/* acknowledge to client */
_tx_sink->acknowledge_packet(packet);
}
/* release acknowledged packets */
_rx_ack(false);
if (_tx_sink->packet_avail())
_send_packet_avail_signal();
}
void _rx_ack(bool block = true)
{
while (_rx.source()->ack_avail() || block) {
Packet_descriptor packet = _rx.source()->get_acked_packet();
/* free packet buffer */
_rx.source()->release_packet(packet);
block = false;
}
}
public:
/**
* Constructor
*/
Session_component(Dataspace_capability tx_ds,
Dataspace_capability rx_ds,
Server::Entrypoint &ep,
Device &device)
:
Nic::Packet_allocator(Genode::env()->heap()),
Packet_session_component(tx_ds, rx_ds, this, ep),
_device(device),
_tx_sink(Session_rpc_object::_tx.sink()),
_tx_alloc(true)
{ _device.session(this); }
/**
* Send packet to client (called from driver)
*/
void rx(addr_t packet, size_t psize, addr_t frag, size_t fsize)
{
size_t size = psize + fsize;
while (true) {
try {
Packet_descriptor p =_rx.source()->alloc_packet(size);
Genode::memcpy(_rx.source()->packet_content(p), (void*)packet, psize);
if (fsize)
Genode::memcpy(_rx.source()->packet_content(p)+psize, (void*)frag, fsize);
_rx.source()->submit_packet(p);
} catch (...) {
/* ack or block */
_rx_ack();
continue;
}
break;
}
_rx_ack(false);
}
/**
* Link state changed (called from driver)
*/
void link_state_changed()
{
if (_link_state_sigh.valid())
Genode::Signal_transmitter(_link_state_sigh).submit();
}
/***************************
** Nic-session interface **
***************************/
Mac_address mac_address() override
{
return _device.mac_address();
}
bool link_state() // override
{
return _device.link_state();
}
void link_state_sigh(Genode::Signal_context_capability sigh)
{
_link_state_sigh = sigh;
}
};
/**
* Root component, handling new session requests
*/
struct Nic::Root : Packet_root<Root_component, Session_component, Device, CACHED>
{
Root(Server::Entrypoint &ep, Allocator *md_alloc, Device &device)
: Packet_root(ep, md_alloc, device) { }
};
#endif /* _NIC__COMPONENT_H_ */

View File

@ -1,143 +0,0 @@
/**
* \brief Packet-stream-session components
* \author Sebastian Sumpf
* \author Norman Feske
* \date 2012-07-06
*/
/*
* Copyright (C) 2012-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.
*/
#ifndef _SIGNAL__DISPATCHER_H_
#define _SIGNAL__DISPATCHER_H_
/* Genode includes */
#include <base/signal.h>
/* local includes */
#include <lx.h>
/**
* Session components that overrides signal handlers
*/
template <typename SESSION_RPC_OBJECT>
class Packet_session_component : public SESSION_RPC_OBJECT
{
private:
Genode::Signal_rpc_member<Packet_session_component> _tx_ready_to_ack_dispatcher;
Genode::Signal_rpc_member<Packet_session_component> _tx_packet_avail_dispatcher;
Genode::Signal_rpc_member<Packet_session_component> _rx_ack_avail_dispatcher;
Genode::Signal_rpc_member<Packet_session_component> _rx_ready_to_submit_dispatcher;
void _tx_ready_to_ack(unsigned)
{
_process_packets(0);
}
void _tx_packet_avail(unsigned)
{
_process_packets(0);
}
void _rx_ack_avail(unsigned)
{
_process_packets(0);
}
void _rx_ready_to_submit(unsigned)
{
_process_packets(0);
}
protected:
virtual void _process_packets(unsigned) = 0;
public:
Packet_session_component(Genode::Dataspace_capability tx_ds,
Genode::Dataspace_capability rx_ds,
Genode::Range_allocator *rx_buffer_alloc,
Server::Entrypoint &ep)
:
SESSION_RPC_OBJECT(tx_ds, rx_ds, rx_buffer_alloc, ep.rpc_ep()),
_tx_ready_to_ack_dispatcher(ep, *this, &Packet_session_component::_tx_ready_to_ack),
_tx_packet_avail_dispatcher(ep, *this, &Packet_session_component::_tx_packet_avail),
_rx_ack_avail_dispatcher(ep, *this, &Packet_session_component::_rx_ack_avail),
_rx_ready_to_submit_dispatcher(ep, *this, &Packet_session_component::_rx_ready_to_submit)
{
SESSION_RPC_OBJECT::_tx.sigh_ready_to_ack(_tx_ready_to_ack_dispatcher);
SESSION_RPC_OBJECT::_tx.sigh_packet_avail(_tx_packet_avail_dispatcher);
SESSION_RPC_OBJECT::_rx.sigh_ack_avail(_rx_ack_avail_dispatcher);
SESSION_RPC_OBJECT::_rx.sigh_ready_to_submit(_rx_ready_to_submit_dispatcher);
}
};
/**
* Root component, handling new session requests
*/
template <typename ROOT_COMPONENT,
typename SESSION_COMPONENT,
typename DEVICE,
Genode::Cache_attribute CACHEABILITY>
class Packet_root : public ROOT_COMPONENT
{
private:
Server::Entrypoint &_ep;
DEVICE &_device;
protected:
/**
* Always returns the singleton block-session component
*/
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);
/* delete ram quota by the memory needed for the session */
size_t session_size = max((size_t)4096,
sizeof(SESSION_COMPONENT)
+ sizeof(Allocator_avl));
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) {
PERR("insufficient 'ram_quota', got %zd, need %zd",
ram_quota, tx_buf_size + session_size);
throw Root::Quota_exceeded();
}
return new (ROOT_COMPONENT::md_alloc())
SESSION_COMPONENT(Lx::backend_alloc(tx_buf_size, CACHEABILITY),
Lx::backend_alloc(rx_buf_size, CACHEABILITY),
_ep, _device);
}
public:
Packet_root(Server::Entrypoint &ep, Genode::Allocator *md_alloc, DEVICE &device)
: ROOT_COMPONENT(&ep.rpc_ep(), md_alloc),
_ep(ep), _device(device) { }
};
#endif /* _SIGNAL__DISPATCHER_H_ */

View File

@ -17,17 +17,17 @@
#include <base/snprintf.h>
#include <cap_session/connection.h>
#include <nic/xml_node.h>
#include <nic_session/nic_session.h>
#include <os/config.h>
#include <nic/component.h>
#include <root/component.h>
#include <util/xml_node.h>
/* local includes */
#include <lx.h>
#include <nic/component.h>
#include <extern_c_begin.h>
# include <lx_emul.h>
# include <net/cfg80211.h>
#include <lx_emul.h>
#include <net/cfg80211.h>
#include <extern_c_end.h>
extern bool config_verbose;
@ -37,22 +37,91 @@ enum {
};
/**
* Net_device to session glue code
* Nic::Session implementation
*/
class Nic_device : public Nic::Device
class Wifi_session_component : public Nic::Session_component
{
public: /* FIXME */
private:
struct net_device *_ndev;
Nic::Session_component *_session = nullptr;
bool _has_link = false;
net_device *_ndev;
bool _has_link = !(_ndev->state & 1UL << __LINK_STATE_NOCARRIER);
protected:
bool _send()
{
using namespace Genode;
if (!_tx.sink()->ready_to_ack())
return false;
if (!_tx.sink()->packet_avail())
return false;
Packet_descriptor packet = _tx.sink()->get_packet();
if (!packet.valid()) {
PWRN("Invalid tx packet");
return true;
}
struct sk_buff *skb = ::alloc_skb(packet.size() + HEAD_ROOM, GFP_KERNEL);
skb_reserve(skb, HEAD_ROOM);
unsigned char *data = skb_put(skb, packet.size());
Genode::memcpy(data, _tx.sink()->packet_content(packet), packet.size());
_ndev->netdev_ops->ndo_start_xmit(skb, _ndev);
_tx.sink()->acknowledge_packet(packet);
return true;
}
void _handle_packet_stream()
{
while (_rx.source()->ack_avail())
_rx.source()->release_packet(_rx.source()->get_acked_packet());
while (_send()) ;
}
public:
Nic_device(struct net_device *ndev) : _ndev(ndev) { }
void rx(sk_buff *skb)
Wifi_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, net_device *ndev)
: Session_component(tx_buf_size, rx_buf_size, rx_block_md_alloc, ram_session, ep),
_ndev(ndev)
{
_ndev->lx_nic_device = this;
}
~Wifi_session_component()
{
_ndev->lx_nic_device = nullptr;
}
/**
* Report link state
*/
void link_state(bool link)
{
/* only report changes of the link state */
if (link == _has_link)
return;
_has_link = link;
_link_state_changed();
}
void receive(struct sk_buff *skb)
{
_handle_packet_stream();
if (!_rx.source()->ready_to_submit())
return;
/* get mac header back */
skb_push(skb, ETH_HLEN);
@ -77,29 +146,24 @@ class Nic_device : public Nic::Device
else
packet_size += skb->len;
_session->rx((Genode::addr_t)packet, packet_size, (Genode::addr_t)frag, frag_size);
try {
Nic::Packet_descriptor p = _rx.source()->alloc_packet(packet_size + frag_size);
void *buffer = _rx.source()->packet_content(p);
memcpy(buffer, packet, packet_size);
if (frag_size)
memcpy((char *)buffer + packet_size, frag, frag_size);
_rx.source()->submit_packet(p);
} catch (...) {
PDBG("failed to process received packet");
}
}
/**
* Report link state
*/
void link_state(bool link)
{
/* only report changes of the link state */
if (link == _has_link)
return;
_has_link = link;
if (_session)
_session->link_state_changed();
}
/**********************
** Device interface **
**********************/
void session(Nic::Session_component *s) override { _session = s; }
/*****************************
** NIC-component interface **
*****************************/
Nic::Mac_address mac_address() override
{
@ -109,32 +173,83 @@ class Nic_device : public Nic::Device
}
bool link_state() override { return _has_link; }
bool tx(Genode::addr_t virt, Genode::size_t size) override
{
struct sk_buff *skb = ::alloc_skb(size + HEAD_ROOM, GFP_KERNEL);
skb_reserve(skb, HEAD_ROOM);
unsigned char *data = skb_put(skb, size);
Genode::memcpy(data, (void*)virt, size);
_ndev->netdev_ops->ndo_start_xmit(skb, _ndev);
return true;
}
};
static Nic_device *_nic = 0;
/**
* NIC root implementation
*/
class Root : public Genode::Root_component<Wifi_session_component,
Genode::Single_client>
{
private:
static Server::Entrypoint *_ep;
Server::Entrypoint &_ep;
void Lx::nic_init(Server::Entrypoint &ep) {
_ep = &ep; }
protected:
Wifi_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(Wifi_session_component));
if (ram_quota < session_size)
throw Genode::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 Genode::Root::Quota_exceeded();
}
session = new (md_alloc())
Wifi_session_component(tx_buf_size, rx_buf_size,
*env()->heap(),
*env()->ram_session(),
_ep, device);
return session;
}
public:
net_device *device = nullptr;
Wifi_session_component *session = nullptr;
static Root *instance;
Root(Server::Entrypoint &ep, Genode::Allocator &md_alloc)
: Genode::Root_component<Wifi_session_component, Genode::Single_client>(&ep.rpc_ep(), &md_alloc),
_ep(ep)
{ }
void announce() { Genode::env()->parent()->announce(_ep.rpc_ep().manage(this)); }
};
Root *Root::instance;
void Lx::nic_init(Server::Entrypoint &ep)
{
static Root root(ep, *Genode::env()->heap());
Root::instance = &root;
}
void Lx::get_mac_address(unsigned char *addr)
{
Genode::memcpy(addr, _nic->_ndev->perm_addr, ETH_ALEN);
memcpy(addr, Root::instance->device->perm_addr, ETH_ALEN);
}
@ -319,12 +434,12 @@ extern "C" void __dev_remove_pack(struct packet_type *pt)
extern "C" struct net_device *__dev_get_by_index(struct net *net, int ifindex)
{
if (!_nic || !_nic->_ndev) {
if (!Root::instance->device) {
PERR("no net device registered!");
return 0;
}
return _nic->_ndev;
return Root::instance->device;
}
@ -387,18 +502,10 @@ extern "C" int register_netdevice(struct net_device *ndev)
return -ENODEV;
}
static Nic_device nic_device(ndev);
static Nic::Root nic_root(*_ep, Genode::env()->heap(), nic_device);
/*
* XXX This is just a starting point for removing all the static stuff from
* this file...
*/
ndev->lx_nic_device = (void *)&nic_device;
_nic = &nic_device;
already_registered = true;
Root::instance->device = ndev;
ndev->state |= 1UL << __LINK_STATE_START;
netif_carrier_off(ndev);
@ -424,7 +531,7 @@ extern "C" int register_netdevice(struct net_device *ndev)
if (ndev->netdev_ops->ndo_set_rx_mode)
ndev->netdev_ops->ndo_set_rx_mode(ndev);
Genode::env()->parent()->announce(_ep->rpc_ep().manage(&nic_root));
Root::instance->announce();
list_add_tail_rcu(&ndev->dev_list, &init_net.dev_base_head);
@ -451,9 +558,10 @@ extern "C" void netif_carrier_on(struct net_device *dev)
{
dev->state &= ~(1UL << __LINK_STATE_NOCARRIER);
Nic_device *nic = (Nic_device *)dev->lx_nic_device;
Wifi_session_component *session = (Wifi_session_component *)dev->lx_nic_device;
nic->link_state(true);
if (session)
session->link_state(true);
}
@ -464,9 +572,10 @@ extern "C" void netif_carrier_off(struct net_device *dev)
dev->state |= 1UL << __LINK_STATE_NOCARRIER;
Nic_device *nic = (Nic_device *)dev->lx_nic_device;
Wifi_session_component *session = (Wifi_session_component *)dev->lx_nic_device;
nic->link_state(false);
if (session)
session->link_state(false);
}
@ -481,14 +590,13 @@ extern "C" int netif_receive_skb(struct sk_buff *skb)
if (ntohs(skb->protocol) == ETH_P_PAE) {
/* XXX call only AF_PACKET hook */
for (Proto_hook* ph = proto_hook_list().first(); ph; ph = ph->next()) {
ph->pt.func(skb, _nic->_ndev, &ph->pt, _nic->_ndev);
ph->pt.func(skb, Root::instance->device, &ph->pt, Root::instance->device);
}
return NET_RX_SUCCESS;
}
if (_nic && _nic->_session) {
_nic->rx(skb);
}
if (Root::instance->session)
Root::instance->session->receive(skb);
dev_kfree_skb(skb);
return NET_RX_SUCCESS;
@ -567,12 +675,6 @@ extern "C" struct net_device *alloc_netdev_mqs(int sizeof_priv, const char *name
}
/**********************
** linux/inerrupt.h **
**********************/
/*************************
** linux/etherdevice.h **
*************************/

View File

@ -1,11 +1,12 @@
/*
* \brief Glue between device-specific NIC driver code and Genode
* \brief Server::Entrypoint based NIC session component
* \author Norman Feske
* \date 2011-05-21
* \author Sebastian Sumpf
* \date 2015-06-22
*/
/*
* Copyright (C) 2011-2013 Genode Labs GmbH
* Copyright (C) 2015 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.
@ -14,101 +15,64 @@
#ifndef _INCLUDE__NIC__COMPONENT_H_
#define _INCLUDE__NIC__COMPONENT_H_
#include <base/env.h>
#include <os/attached_ram_dataspace.h>
#include <os/server.h>
#include <nic/packet_allocator.h>
#include <nic_session/rpc_object.h>
#include <base/allocator_avl.h>
#include <util/arg_string.h>
#include <base/rpc_server.h>
#include <root/component.h>
#include <nic/driver.h>
enum { VERBOSE_RX = false };
namespace Nic {
class Communication_buffers;
class Session_component;
/**
* Shortcut for single-client NIC root component
*/
typedef Genode::Root_component<Session_component, Genode::Single_client>
Root_component;
class Root;
}
class Nic::Session_component : public Genode::Allocator_avl,
public Session_rpc_object, public Rx_buffer_alloc,
public Driver_notification
class Nic::Communication_buffers
{
private:
protected:
Driver_factory &_driver_factory;
Driver &_driver;
Nic::Packet_allocator _rx_packet_alloc;
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
{
protected:
Server::Entrypoint &_ep;
Genode::Signal_context_capability _link_state_sigh;
/* rx packet descriptor */
Genode::Packet_descriptor _curr_rx_packet;
enum { TX_STACK_SIZE = 8*1024 };
class Tx_thread : public Genode::Thread<TX_STACK_SIZE>
/**
* Signal link-state change to client
*/
void _link_state_changed()
{
private:
Tx::Sink *_tx_sink;
Driver &_driver;
public:
Tx_thread(Tx::Sink *tx_sink, Driver &driver)
:
Genode::Thread<TX_STACK_SIZE>("tx"),
_tx_sink(tx_sink), _driver(driver)
{
start();
}
void entry()
{
using namespace Genode;
while (true) {
/* block for packet from client */
Packet_descriptor packet = _tx_sink->get_packet();
if (!packet.valid()) {
PWRN("received invalid packet");
continue;
}
_driver.tx(_tx_sink->packet_content(packet),
packet.size());
/* acknowledge packet to the client */
if (!_tx_sink->ready_to_ack())
PDBG("need to wait until ready-for-ack");
_tx_sink->acknowledge_packet(packet);
}
}
} _tx_thread;
void dump()
{
using namespace Genode;
if (!VERBOSE_RX) return;
char *buf = (char *)_rx.source()->packet_content(_curr_rx_packet);
size_t size = _curr_rx_packet.size();
printf("rx packet:");
for (unsigned i = 0; i < size; i++)
printf("%02x,", buf[i]);
printf("\n");
if (_link_state_sigh.valid())
Genode::Signal_transmitter(_link_state_sigh).submit();
}
/**
* Sub-classes must implement this function, it is called upon all
* packet-stream signals.
*/
virtual void _handle_packet_stream() = 0;
void _dispatch(unsigned) { _handle_packet_stream(); }
Genode::Signal_rpc_member<Session_component> _packet_stream_dispatcher {
_ep, *this, &Session_component::_dispatch };
public:
/**
@ -116,149 +80,47 @@ class Nic::Session_component : public Genode::Allocator_avl,
*
* \param tx_buf_size buffer size for tx channel
* \param rx_buf_size buffer size for rx channel
* \param rx_block_alloc rx block allocator
* \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 tx_buf_size,
Genode::size_t rx_buf_size,
Nic::Driver_factory &driver_factory,
Genode::Rpc_entrypoint &ep)
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)
:
Genode::Allocator_avl(Genode::env()->heap()),
Session_rpc_object(Genode::env()->ram_session()->alloc(tx_buf_size),
Genode::env()->ram_session()->alloc(rx_buf_size),
static_cast<Genode::Range_allocator *>(this), ep),
_driver_factory(driver_factory),
_driver(*driver_factory.create(*this, *this)),
_tx_thread(_tx.sink(), _driver)
{ }
/**
* Destructor
*/
~Session_component()
Communication_buffers(rx_block_md_alloc, ram_session,
tx_buf_size, rx_buf_size),
Session_rpc_object(_tx_ds.cap(),
_rx_ds.cap(),
&_rx_packet_alloc, ep.rpc_ep()),
_ep(ep)
{
_driver_factory.destroy(&_driver);
/* 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);
}
/***********************************
** Driver-notification interface **
***********************************/
void link_state_changed() override
{
if (_link_state_sigh.valid())
Genode::Signal_transmitter(_link_state_sigh).submit();
}
/*******************************
** Rx_buffer_alloc interface **
*******************************/
void *alloc(Genode::size_t size) override
{
/* assign rx packet descriptor */
_curr_rx_packet = _rx.source()->alloc_packet(size);
return _rx.source()->packet_content(_curr_rx_packet);
}
void submit() override
{
/* check for acknowledgements from the client */
while (_rx.source()->ack_avail()) {
Genode::Packet_descriptor packet = _rx.source()->get_acked_packet();
/* free packet buffer */
_rx.source()->release_packet(packet);
}
dump();
_rx.source()->submit_packet(_curr_rx_packet);
/* invalidate rx packet descriptor */
_curr_rx_packet = Packet_descriptor();
}
/****************************
** Nic::Session interface **
****************************/
Mac_address mac_address() { return _driver.mac_address(); }
bool link_state() { return _driver.link_state(); }
Tx::Sink* tx_sink() { return _tx.sink(); }
Rx::Source* rx_source() { return _rx.source(); }
void link_state_sigh(Genode::Signal_context_capability sigh) override
void link_state_sigh(Genode::Signal_context_capability sigh)
{
_link_state_sigh = sigh;
}
};
/*
* Root component, handling new session requests.
*/
class Nic::Root : public Root_component
{
private:
Driver_factory &_driver_factory;
Genode::Rpc_entrypoint &_ep;
protected:
/*
* Always returns the singleton nic-session component.
/**
* Return the current link state
*/
Session_component *_create_session(const char *args)
{
using namespace Genode;
virtual bool link_state() = 0;
Genode::size_t ram_quota =
Arg_string::find_arg(args, "ram_quota" ).ulong_value(0);
Genode::size_t tx_buf_size =
Arg_string::find_arg(args, "tx_buf_size").ulong_value(0);
Genode::size_t rx_buf_size =
Arg_string::find_arg(args, "rx_buf_size").ulong_value(0);
/* delete ram quota by the memory needed for the session */
Genode::size_t session_size = max((Genode::size_t)4096, sizeof(Session_component)
+ sizeof(Allocator_avl));
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,
_driver_factory,
_ep);
}
public:
Root(Genode::Rpc_entrypoint *session_ep,
Genode::Allocator *md_alloc,
Nic::Driver_factory &driver_factory)
:
Root_component(session_ep, md_alloc),
_driver_factory(driver_factory),
_ep(*session_ep)
{ }
/**
* Return the MAC address of the device
*/
virtual Mac_address mac_address() = 0;
};
#endif /* _INCLUDE__NIC__COMPONENT_H_ */

View File

@ -1,109 +0,0 @@
/*
* \brief Interfaces used internally in NIC drivers
* \author Norman Feske
* \date 2011-05-21
*/
/*
* Copyright (C) 2011-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.
*/
#ifndef _INCLUDE__NIC__DRIVER_H_
#define _INCLUDE__NIC__DRIVER_H_
#include <os/irq_activation.h> /* for 'Genode::Irq_handler' type */
#include <nic_session/nic_session.h> /* for 'Nic::Mac_address' type */
namespace Nic {
struct Rx_buffer_alloc;
struct Driver;
struct Driver_factory;
struct Driver_notification;
}
/**
* Interface for allocating the backing store for incoming packets
*/
struct Nic::Rx_buffer_alloc
{
/**
* Allocate packet buffer
*/
virtual void *alloc(Genode::size_t) = 0;
/**
* Submit packet to client
*/
virtual void submit() = 0;
};
/**
* Interface for driver-to-component notifications
*/
struct Nic::Driver_notification { virtual void link_state_changed() = 0; };
/**
* Interface to be implemented by the device-specific driver code
*/
struct Nic::Driver : Genode::Irq_handler
{
/**
* Return MAC address of the network interface
*/
virtual Mac_address mac_address() = 0;
/**
* Return link state (true if link detected)
*/
virtual bool link_state() = 0;
/**
* Transmit packet
*
* \param packet start of packet
* \param size packet size
*
* If the packet size is not a multiple of 4 bytes, this method
* accesses the bytes after the packet buffer up to the next 4-byte
* length (in the worst case, 3 bytes after the packet end).
*/
virtual void tx(char const *packet, Genode::size_t size) = 0;
};
/**
* Interface for constructing the driver object
*
* The driver object requires an rx-packet allocator at construction time.
* This allocator, however, exists not before the creation of a NIC session
* because the client pays for it. Therefore, the driver must be created at
* session-construction time. Because drivers may differ with regard to
* their constructor arguments, the 'Driver_factory' interface allows for
* unifying the session-creation among these drivers.
*/
struct Nic::Driver_factory
{
/**
* Construct new driver
*
* \param rx_buffer_alloc buffer allocator used for storing incoming
* packets
* \param notifier callback for notifications
*/
virtual Driver *create(Rx_buffer_alloc &rx_buffer_alloc,
Driver_notification &notify) = 0;
/**
* Destroy driver
*/
virtual void destroy(Driver *driver) = 0;
};
#endif /* _INCLUDE__NIC__DRIVER_H_ */

View File

@ -0,0 +1,77 @@
/**
* \brief Simple single client NIC Root
* \author Sebastian Sumpf
* \date 2015-06-23
*/
/*
* Copyright (C) 2015 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.
*/
#ifndef _INCLUDE__NIC__ROOT_H_
#define _INCLUDE__NIC__ROOT_H_
#include <nic/component.h>
#include <root/component.h>
namespace Nic {
template <class SESSION_COMPONENT> class Root;
};
template <class SESSION_COMPONENT>
class Nic::Root : public Genode::Root_component<SESSION_COMPONENT,
Genode::Single_client>
{
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 Genode::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 Genode::Root::Quota_exceeded();
}
return new (Root::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<SESSION_COMPONENT, Genode::Single_client>(&ep.rpc_ep(), &md_alloc),
_ep(ep)
{ }
};
#endif /* _INCLUDE__NIC__ROOT_H_ */

View File

@ -19,9 +19,10 @@
#include <os/attached_io_mem_dataspace.h>
#include <os/irq_activation.h>
#include <timer_session/connection.h>
#include <nic/driver.h>
#include <nic/component.h>
class Lan9118 : public Nic::Driver
class Lan9118 : public Nic::Session_component,
public Genode::Irq_handler
{
private:
@ -70,9 +71,7 @@ class Lan9118 : public Nic::Driver
Genode::Attached_io_mem_dataspace _mmio;
volatile Genode::uint32_t *_reg_base;
Timer::Connection _timer;
Nic::Rx_buffer_alloc &_rx_buffer_alloc;
Nic::Mac_address _mac_addr;
Nic::Driver_notification &_notify;
enum { IRQ_STACK_SIZE = 4096 };
Genode::Irq_activation _irq_activation;
@ -189,6 +188,67 @@ class Lan9118 : public Nic::Driver
return _reg_read(RX_FIFO_INF) & 0xffff;
}
protected:
bool _send()
{
if (!_tx.sink()->ready_to_ack())
return false;
if (!_tx.sink()->packet_avail())
return false;
Genode::Packet_descriptor packet = _tx.sink()->get_packet();
if (!packet.valid()) {
PWRN("Invalid tx packet");
return true;
}
/* limit size to 11 bits, the maximum supported by lan9118 */
enum { MAX_PACKET_SIZE_LOG2 = 11 };
Genode::size_t const max_size = (1 << MAX_PACKET_SIZE_LOG2) - 1;
if (packet.size() > max_size) {
PERR("packet size %zd too large, limit is %zd", packet.size(), max_size);
return true;
}
enum { FIRST_SEG = (1 << 13),
LAST_SEG = (1 << 12) };
Genode::uint32_t const cmd_a = packet.size() | FIRST_SEG | LAST_SEG,
cmd_b = packet.size();
unsigned count = Genode::align_addr(packet.size(), 2) >> 2;
Genode::uint32_t *src = (Genode::uint32_t *)_tx.sink()->packet_content(packet);
/* check space left in tx data fifo */
Genode::size_t const fifo_avail = _reg_read(TX_FIFO_INF) & 0xffff;
if (fifo_avail < count*4 + sizeof(cmd_a) + sizeof(cmd_b)) {
PERR("tx fifo overrun, ignore packet");
_tx.sink()->acknowledge_packet(packet);
return false;
}
_reg_write(TX_DATA_FIFO, cmd_a);
_reg_write(TX_DATA_FIFO, cmd_b);
/* supply payload to transmit fifo */
for (; count--; src++)
_reg_write(TX_DATA_FIFO, *src);
_tx.sink()->acknowledge_packet(packet);
return true;
}
void _handle_packet_stream() override
{
while (_rx.source()->ack_avail())
_rx.source()->release_packet(_rx.source()->get_acked_packet());
while (_send()) ;
}
public:
/**
@ -202,13 +262,14 @@ class Lan9118 : public Nic::Driver
* \throw Device_not_supported
*/
Lan9118(Genode::addr_t mmio_base, Genode::size_t mmio_size, int irq,
Nic::Rx_buffer_alloc &rx_buffer_alloc,
Nic::Driver_notification &notify)
:
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),
_mmio(mmio_base, mmio_size),
_reg_base(_mmio.local_addr<Genode::uint32_t>()),
_rx_buffer_alloc(rx_buffer_alloc),
_notify(notify),
_irq_activation(irq, *this, IRQ_STACK_SIZE)
{
unsigned long const id_rev = _reg_read(ID_REV),
@ -290,58 +351,21 @@ class Lan9118 : public Nic::Driver
_mac_csr_write(MAC_CR, 0);
}
void link_state_changed() { _notify.link_state_changed(); }
/**************************************
** Nic::Session_component interface **
**************************************/
/***************************
** Nic::Driver interface **
***************************/
Nic::Mac_address mac_address()
Nic::Mac_address mac_address() override
{
return _mac_addr;
}
bool link_state()
bool link_state() override
{
/* XXX always return true for now */
return true;
}
void tx(char const *packet, Genode::size_t size)
{
/* limit size to 11 bits, the maximum supported by lan9118 */
enum { MAX_PACKET_SIZE_LOG2 = 11 };
Genode::size_t const max_size = (1 << MAX_PACKET_SIZE_LOG2) - 1;
if (size > max_size) {
PERR("packet size %zd too large, limit is %zd", size, max_size);
return;
}
enum { FIRST_SEG = (1 << 13),
LAST_SEG = (1 << 12) };
Genode::uint32_t const cmd_a = size | FIRST_SEG | LAST_SEG,
cmd_b = size;
unsigned count = Genode::align_addr(size, 2) >> 2;
Genode::uint32_t *src = (Genode::uint32_t *)packet;
/* check space left in tx data fifo */
Genode::size_t const fifo_avail = _reg_read(TX_FIFO_INF) & 0xffff;
if (fifo_avail < count*4 + sizeof(cmd_a) + sizeof(cmd_b)) {
PERR("tx fifo overrun, ignore packet");
return;
}
_reg_write(TX_DATA_FIFO, cmd_a);
_reg_write(TX_DATA_FIFO, cmd_b);
/* supply payload to transmit fifo */
for (; count--; src++)
_reg_write(TX_DATA_FIFO, *src);
}
/******************************
** Irq_activation interface **
@ -351,7 +375,9 @@ class Lan9118 : public Nic::Driver
{
using namespace Genode;
while (_rx_packet_avail()) {
_handle_packet_stream();
while (_rx_packet_avail() && _rx.source()->ready_to_submit()) {
/* read packet from NIC, copy to client buffer */
Rx_packet_info packet = _rx_packet_info();
@ -360,7 +386,12 @@ class Lan9118 : public Nic::Driver
size_t const size = align_addr(packet.size, 2);
/* allocate rx packet buffer */
uint32_t *dst = (uint32_t *)_rx_buffer_alloc.alloc(size);
Nic::Packet_descriptor p;
try {
p = _rx.source()->alloc_packet(size);
} catch (Session::Rx::Source::Packet_alloc_failed) { return; }
uint32_t *dst = (uint32_t *)_rx.source()->packet_content(p);
/* calculate number of words to be read from rx fifo */
size_t count = min(size, _rx_data_pending()) >> 2;
@ -369,7 +400,7 @@ class Lan9118 : public Nic::Driver
for (; count--; )
*dst++ = _reg_read(RX_DATA_FIFO);
_rx_buffer_alloc.submit();
_rx.source()->submit_packet(p);
}
/* acknowledge all pending irqs */

View File

@ -18,6 +18,8 @@
#include <base/sleep.h>
#include <cap_session/connection.h>
#include <nic/component.h>
#include <os/server.h>
#include <root/component.h>
/* device definitions */
#include <lan9118_defs.h>
@ -25,39 +27,80 @@
/* driver code */
#include <lan9118.h>
namespace Server { struct Main; }
int main(int, char **)
class Root : public Genode::Root_component<Lan9118, Genode::Single_client>
{
using namespace Genode;
private:
printf("--- LAN9118 NIC driver started ---\n");
Server::Entrypoint &_ep;
/**
* Factory used by 'Nic::Root' at session creation/destruction time
*/
struct Lan9118_driver_factory : Nic::Driver_factory
protected:
Lan9118 *_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(Lan9118));
if (ram_quota < session_size)
throw Genode::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 Genode::Root::Quota_exceeded();
}
return new (Root::md_alloc())
Lan9118(LAN9118_PHYS, LAN9118_SIZE, LAN9118_IRQ,
tx_buf_size, rx_buf_size,
*env()->heap(),
*env()->ram_session(),
_ep);
}
public:
Root(Server::Entrypoint &ep, Genode::Allocator &md_alloc)
: Genode::Root_component<Lan9118, Genode::Single_client>(&ep.rpc_ep(), &md_alloc),
_ep(ep)
{ }
};
struct Server::Main
{
Entrypoint &ep;
::Root nic_root{ ep, *Genode::env()->heap() };
Main(Entrypoint &ep) : ep(ep)
{
Nic::Driver *create(Nic::Rx_buffer_alloc &alloc,
Nic::Driver_notification &notify)
{
return new (env()->heap())
Lan9118(LAN9118_PHYS, LAN9118_SIZE, LAN9118_IRQ, alloc, notify);
}
printf("--- LAN9118 NIC driver started ---\n");
Genode::env()->parent()->announce(ep.manage(nic_root));
}
};
void destroy(Nic::Driver *driver)
{
Genode::destroy(env()->heap(), static_cast<Lan9118 *>(driver));
}
} driver_factory;
namespace Server {
enum { STACK_SIZE = 4096 };
static Cap_connection cap;
static Rpc_entrypoint ep(&cap, STACK_SIZE, "nic_ep");
char const *name() { return "nic_ep"; }
static Nic::Root nic_root(&ep, env()->heap(), driver_factory);
env()->parent()->announce(ep.manage(&nic_root));
size_t stack_size() { return 2*1024*sizeof(long); }
Genode::sleep_forever();
return 0;
void construct(Entrypoint &ep)
{
static Main main(ep);
}
}

View File

@ -1,5 +1,5 @@
REQUIRES = lan9118
TARGET = nic_drv
SRC_CC = main.cc
LIBS = base
LIBS = base server
INC_DIR += $(PRG_DIR)

View File

@ -2,6 +2,7 @@
* \brief NIC driver for Linux TUN/TAP device
* \author Stefan Kalkowski
* \author Christian Helmuth
* \author Sebastian Sumpf
* \date 2011-08-08
*
* Configuration options are:
@ -23,9 +24,8 @@
*/
/* Genode */
#include <base/sleep.h>
#include <cap_session/connection.h>
#include <nic/component.h>
#include <base/thread.h>
#include <nic/root.h>
#include <nic/xml_node.h>
#include <os/config.h>
@ -37,18 +37,20 @@
#include <net/if.h>
#include <linux/if_tun.h>
namespace Server { struct Main; }
class Linux_driver : public Nic::Driver
class Linux_session_component : public Nic::Session_component
{
private:
struct Rx_thread : Genode::Thread<0x2000>
struct Rx_signal_thread : Genode::Thread<0x1000>
{
int fd;
Nic::Driver &driver;
int fd;
Genode::Signal_context_capability sigh;
Rx_thread(int fd, Nic::Driver &driver)
: Genode::Thread<0x2000>("rx"), fd(fd), driver(driver) { }
Rx_signal_thread(int fd, Genode::Signal_context_capability sigh)
: Genode::Thread<0x1000>("rx_signal"), fd(fd), sigh(sigh) { }
void entry()
{
@ -61,19 +63,15 @@ class Linux_driver : public Nic::Driver
FD_SET(fd, &rfds);
do { ret = select(fd + 1, &rfds, 0, 0, 0); } while (ret < 0);
/* inform driver about incoming packet */
driver.handle_irq(fd);
/* signal incoming packet */
Genode::Signal_transmitter(sigh).submit();
}
}
};
Nic::Mac_address _mac_addr;
Nic::Rx_buffer_alloc &_alloc;
Nic::Driver_notification &_notify;
char _packet_buffer[1514]; /* maximum ethernet packet length */
int _tap_fd;
Rx_thread _rx_thread;
Nic::Mac_address _mac_addr;
int _tap_fd;
Rx_signal_thread _rx_thread;
int _setup_tap_fd()
{
@ -88,6 +86,12 @@ class Linux_driver : public Nic::Driver
throw Genode::Exception();
}
/* set fd to non-blocking */
if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) {
PERR("could not set /dev/net/tun to non-blocking");
throw Genode::Exception();
}
Genode::memset(&ifr, 0, sizeof(ifr));
ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
@ -113,13 +117,85 @@ class Linux_driver : public Nic::Driver
return fd;
}
bool _send()
{
using namespace Genode;
if (!_tx.sink()->ready_to_ack())
return false;
if (!_tx.sink()->packet_avail())
return false;
Packet_descriptor packet = _tx.sink()->get_packet();
if (!packet.valid()) {
PWRN("Invalid tx packet");
return true;
}
int ret;
/* non-blocking-write packet to TAP */
do {
ret = write(_tap_fd, _tx.sink()->packet_content(packet), packet.size());
/* drop packet if write would block */
if (ret < 0 && errno == EAGAIN)
continue;
if (ret < 0) PERR("write: errno=%d", errno);
} while (ret < 0);
_tx.sink()->acknowledge_packet(packet);
return true;
}
bool _receive()
{
unsigned const max_size = Nic::Packet_allocator::DEFAULT_PACKET_SIZE;
if (!_rx.source()->ready_to_submit())
return false;
Nic::Packet_descriptor p;
try {
p = _rx.source()->alloc_packet(max_size);
} catch (Session::Rx::Source::Packet_alloc_failed) { return false; }
int size = read(_tap_fd, _rx.source()->packet_content(p), max_size);
if (size <= 0) {
_rx.source()->release_packet(p);
return false;
}
/* adjust packet size */
Nic::Packet_descriptor p_adjust(p.offset(), size);
_rx.source()->submit_packet(p_adjust);
return true;
}
protected:
void _handle_packet_stream() override
{
while (_rx.source()->ack_avail())
_rx.source()->release_packet(_rx.source()->get_acked_packet());
while (_send()) ;
while (_receive()) ;
}
public:
Linux_driver(Nic::Rx_buffer_alloc &alloc,
Nic::Driver_notification &notify)
Linux_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)
:
_alloc(alloc), _notify(notify),
_tap_fd(_setup_tap_fd()), _rx_thread(_tap_fd, *this)
Session_component(tx_buf_size, rx_buf_size, rx_block_md_alloc, ram_session, ep),
_tap_fd(_setup_tap_fd()), _rx_thread(_tap_fd, _packet_stream_dispatcher)
{
/* try using configured MAC address */
try {
@ -145,93 +221,23 @@ class Linux_driver : public Nic::Driver
_rx_thread.start();
}
void link_state_changed() { _notify.link_state_changed(); }
/***************************
** Nic::Driver interface **
***************************/
Nic::Mac_address mac_address() { return _mac_addr; }
bool link_state()
{
/* XXX return always true for now */
return true;
}
void tx(char const *packet, Genode::size_t size)
{
int ret;
/* blocking-write packet to TAP */
do {
ret = write(_tap_fd, packet, size);
if (ret < 0) PERR("write: errno=%d", errno);
} while (ret < 0);
}
/******************************
** Irq_activation interface **
******************************/
void handle_irq(int)
{
int ret;
/* blocking read incoming packet */
do {
ret = read(_tap_fd, _packet_buffer, sizeof(_packet_buffer));
if (ret < 0) PERR("read: errno=%d", errno);
} while (ret < 0);
void *buffer = _alloc.alloc(ret);
Genode::memcpy(buffer, _packet_buffer, ret);
_alloc.submit();
}
bool link_state() override { return true; }
Nic::Mac_address mac_address() override { return _mac_addr; }
};
/*
* Manually initialize the 'lx_environ' pointer, needed because 'nic_drv' is not
* using the normal Genode startup code.
*/
extern char **environ;
char **lx_environ = environ;
int main(int, char **)
struct Server::Main
{
using namespace Genode;
Entrypoint &ep;
Nic::Root<Linux_session_component> nic_root{ ep, *Genode::env()->heap() };
printf("--- Linux/tap NIC driver started ---\n");
/**
* Factory used by 'Nic::Root' at session creation/destruction time
*/
struct Linux_driver_factory : Nic::Driver_factory
Main(Entrypoint &ep) : ep(ep)
{
Nic::Driver *create(Nic::Rx_buffer_alloc &alloc,
Nic::Driver_notification &notify)
{
return new (env()->heap()) Linux_driver(alloc, notify);
}
Genode::env()->parent()->announce(ep.manage(nic_root));
}
};
void destroy(Nic::Driver *driver)
{
Genode::destroy(env()->heap(), static_cast<Linux_driver *>(driver));
}
} driver_factory;
enum { STACK_SIZE = 2*4096 };
static Cap_connection cap;
static Rpc_entrypoint ep(&cap, STACK_SIZE, "nic_ep");
static Nic::Root nic_root(&ep, env()->heap(), driver_factory);
env()->parent()->announce(ep.manage(&nic_root));
sleep_forever();
return 0;
}
char const * Server::name() { return "nic_ep"; }
size_t Server::stack_size() { return 2*1024*sizeof(long); }
void Server::construct(Entrypoint &ep) { static Main main(ep); }

View File

@ -1,4 +1,4 @@
TARGET = nic_drv
REQUIRES = linux
LIBS = lx_hybrid config
LIBS = lx_hybrid config server
SRC_CC = main.cc

View File

@ -15,19 +15,16 @@
#include <base/env.h>
#include <base/sleep.h>
#include <os/attached_ram_dataspace.h>
#include <os/server.h>
#include <os/signal_rpc_dispatcher.h>
#include <root/component.h>
#include <util/arg_string.h>
#include <util/misc_math.h>
#include <nic_session/rpc_object.h>
#include <nic_session/client.h>
#include <nic/component.h>
#include <nic/packet_allocator.h>
namespace Nic {
class Communication_buffers;
class Session_component;
class Loopback_component;
class Root;
}
@ -35,36 +32,8 @@ namespace Nic {
namespace Server { struct Main; }
class Nic::Communication_buffers
class Nic::Loopback_component : public Nic::Session_component
{
protected:
Genode::Allocator_avl _rx_packet_alloc;
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<Session_component> _packet_stream_dispatcher {
_ep, *this, &Session_component::_handle_packet_stream };
public:
/**
@ -78,46 +47,37 @@ class Nic::Session_component : Communication_buffers, public Session_rpc_object
* \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);
}
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)
{ }
Mac_address mac_address()
Mac_address mac_address() override
{
Mac_address result = {{1,2,3,4,5,6}};
return result;
}
bool link_state()
bool link_state() override
{
/* XXX always return true, for now */
return true;
}
void link_state_sigh(Genode::Signal_context_capability sigh) { }
void _handle_packet_stream() override;
};
void Nic::Session_component::_handle_packet_stream(unsigned)
void Nic::Loopback_component::_handle_packet_stream()
{
using namespace Genode;
unsigned const alloc_size = Nic::Packet_allocator::DEFAULT_PACKET_SIZE;
/* loop unless we cannot make any progress */
for (;;) {
@ -154,35 +114,36 @@ void Nic::Session_component::_handle_packet_stream(unsigned)
* We are safe to process one packet without blocking.
*/
Packet_descriptor packet_to_client;
try {
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.valid()) {
PWRN("received invalid packet");
_rx.source()->release_packet(packet_to_client);
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);
packet_from_client.size());
packet_to_client = Packet_descriptor(packet_to_client.offset(), packet_from_client.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<Session_component>
class Nic::Root : public Genode::Root_component<Loopback_component>
{
private:
@ -190,7 +151,7 @@ class Nic::Root : public Genode::Root_component<Session_component>
protected:
Session_component *_create_session(const char *args)
Loopback_component*_create_session(const char *args)
{
using namespace Genode;
@ -216,7 +177,7 @@ class Nic::Root : public Genode::Root_component<Session_component>
throw Root::Quota_exceeded();
}
return new (md_alloc()) Session_component(tx_buf_size, rx_buf_size,
return new (md_alloc()) Loopback_component(tx_buf_size, rx_buf_size,
*env()->heap(),
*env()->ram_session(),
_ep);
@ -226,7 +187,7 @@ class Nic::Root : public Genode::Root_component<Session_component>
Root(Server::Entrypoint &ep, Genode::Allocator &md_alloc)
:
Genode::Root_component<Session_component>(&ep.rpc_ep(), &md_alloc),
Genode::Root_component<Loopback_component>(&ep.rpc_ep(), &md_alloc),
_ep(ep)
{ }
};

View File

@ -82,14 +82,12 @@ Tuntap_device *tuntap_dev()
** Implementation of the Nic service **
***************************************/
class Nic_driver : public Tuntap_device,
public Nic::Driver
class Openvpn_component : public Tuntap_device,
public Nic::Session_component
{
private:
Nic::Mac_address _mac_addr {{ 0x02, 0x00, 0x00, 0x00, 0x00, 0x01 }};
Nic::Rx_buffer_alloc &_alloc;
Nic::Driver_notification &_notify;
char const *_packet;
@ -99,12 +97,52 @@ class Nic_driver : public Tuntap_device,
Genode::Semaphore _startup_lock;
Genode::Semaphore _tx_lock;
protected:
bool _send()
{
using namespace Genode;
if (!_tx.sink()->ready_to_ack())
return false;
if (!_tx.sink()->packet_avail())
return false;
Packet_descriptor packet = _tx.sink()->get_packet();
if (!packet.valid()) {
PWRN("Invalid tx packet");
return true;
}
_packet = _tx.sink()->packet_content(packet);
/* notify openvpn */
::write(_pipefd[WRITE], "1", 1);
/* block while openvpn handles the packet */
_tx_lock.down();
_tx.sink()->acknowledge_packet(packet);
return true;
}
void _handle_packet_stream() override
{
while (_rx.source()->ack_avail())
_rx.source()->release_packet(_rx.source()->get_acked_packet());
while (_send()) ;
}
public:
Nic_driver(Nic::Rx_buffer_alloc &alloc, Nic::Driver_notification &notify)
:
_alloc(alloc), _notify(notify), _packet(0)
Openvpn_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)
{
if (pipe(_pipefd)) {
PERR("could not create pipe");
@ -112,41 +150,18 @@ class Nic_driver : public Tuntap_device,
}
}
~Nic_driver() { PDBG("should probably be implemented"); }
/**************************************
** Nic::Session_component interface **
**************************************/
void link_state_changed() { _notify.link_state_changed(); }
Nic::Mac_address mac_address() override { return _mac_addr; }
/***************************
** Nic::Driver interface **
***************************/
Nic::Mac_address mac_address() { return _mac_addr; }
bool link_state()
bool link_state() override
{
/* XXX always return true for now */
return true;
}
void tx(char const *packet, Genode::size_t size)
{
PDBGV("packet:0x%p size:%zu", packet, size);
_packet = packet;
/* notify openvpn */
::write(_pipefd[WRITE], "1", 1);
/* block while openvpn handles the packet */
_tx_lock.down();
}
/******************************
** Irq_activation interface **
******************************/
void handle_irq(int) { }
/***********************
** TUN/TAP interface **
***********************/
@ -171,10 +186,16 @@ class Nic_driver : public Tuntap_device,
int write(char const *buf, Genode::size_t len)
{
PDBGV("buf:0x%p len:%zu", len);
_handle_packet_stream();
void *buffer = _alloc.alloc(len);
Genode::memcpy(buffer, buf, len);
_alloc.submit();
if (!_rx.source()->ready_to_submit())
return 0;
try {
Genode::Packet_descriptor packet = _rx.source()->alloc_packet(len);
Genode::memcpy(_rx.source()->packet_content(packet), buf, len);
_rx.source()->submit_packet(packet);
} catch (...) { return 0; }
return len;
}
@ -185,57 +206,86 @@ class Nic_driver : public Tuntap_device,
};
struct Main
class Root : public Genode::Root_component<Openvpn_component, Genode::Single_client>
{
struct Nic_driver_factory : Nic::Driver_factory
{
Nic_driver *drv { 0 };
Openvpn_thread *openvpn { 0 };
private:
Nic::Driver *create(Nic::Rx_buffer_alloc &alloc,
Nic::Driver_notification &notify)
Server::Entrypoint &_ep;
Openvpn_thread *_thread = nullptr;
protected:
Openvpn_component *_create_session(const char *args)
{
/* there can be only one */
if (!drv) {
drv = new (Genode::env()->heap()) Nic_driver(alloc, notify);
using namespace Genode;
/**
* Setting the pointer in this manner is quite hackish but it has
* to be valid before OpenVPN calls open_tun(), which unfortunatly
* is early.
*/
_tuntap_dev = drv;
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);
PDBGV("start OpenVPN main thread");
Openvpn_thread *openvpn = new (Genode::env()->heap()) Openvpn_thread(genode_argc,
genode_argv);
/* deplete ram quota by the memory needed for the session structure */
size_t session_size = max(4096UL, (unsigned long)sizeof(Openvpn_component));
if (ram_quota < session_size)
throw Genode::Root::Quota_exceeded();
openvpn->start();
/* wait until OpenVPN configured the TUN/TAP device for the first time */
_tuntap_dev->down();
return drv;
/*
* 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 Genode::Root::Quota_exceeded();
}
return 0;
Openvpn_component *component = new (Root::md_alloc())
Openvpn_component(tx_buf_size, rx_buf_size,
*env()->heap(),
*env()->ram_session(),
_ep);
/**
* Setting the pointer in this manner is quite hackish but it has
* to be valid before OpenVPN calls open_tun(), which unfortunatly
* is early.
*/
_tuntap_dev = component;
PDBGV("start OpenVPN main thread");
_thread = new (Genode::env()->heap()) Openvpn_thread(genode_argc, genode_argv);
_thread->start();
/* wait until OpenVPN configured the TUN/TAP device for the first time */
_tuntap_dev->down();
return component;
}
void destroy(Nic::Driver *driver)
void _destroy_session(Openvpn_component *session)
{
Genode::destroy(Genode::env()->heap(), static_cast<Nic_driver *>(driver));
drv = 0;
Genode::destroy(Genode::env()->heap(), openvpn);
openvpn = 0;
Genode::destroy(Root::md_alloc(), session);
Genode::destroy(Root::md_alloc(), _thread);
_thread = nullptr;
}
} driver_factory;
public:
Root(Server::Entrypoint &ep, Genode::Allocator &md_alloc)
: Genode::Root_component<Openvpn_component, Genode::Single_client>(&ep.rpc_ep(), &md_alloc),
_ep(ep)
{ }
};
struct Main
{
Server::Entrypoint &ep;
::Root nic_root { ep, *Genode::env()->heap() };
Main(Server::Entrypoint &ep) : ep(ep)
{
static Nic::Root nic_root(&ep.rpc_ep(), Genode::env()->heap(), driver_factory);
Genode::env()->parent()->announce(ep.manage(nic_root));
}
};