nic_router: preserve session link state until read

See the NIC router README paragraph "Behavior regarding the NIC-session link
state" for further information.

Ref #3931
This commit is contained in:
Martin Stein 2020-11-17 12:27:43 +01:00 committed by Norman Feske
parent a7b878cbb5
commit b0327d0544
9 changed files with 315 additions and 56 deletions

View File

@ -517,6 +517,46 @@ When set to zero, the limit is deactivated, meaning that the router always
handles all available packets of a NIC session.
Behavior regarding the NIC-session link state
---------------------------------------------
At downlinks, the NIC router applies a feedback-driven state machine for the
link state in order to ensure that clients recognize state transitions. This
means that the NIC router guarantees that a new link state remains fixed until
the client has read it using the corresponding RPC at the NIC-session
interface. If, in the meantime, further "up" and "down" edges occur, they are
memorized and executed as soon as the former state has been read by the
client. Such postponed link state edges are merged in a way that they result in
two contrary edges at a max. The following diagrams demonstrate this:
! client reads: 0 1 0 1 0 1
! _____ _______ _________________
! client link state: ____| |______| |_________|
! _ __________ _________________
! real link state: ____| |_____| |___________|
!
! time --->
! client reads: 0 1 0 1 0 1
! ______ _____________ _____
! client link state: ____| |______| |________| |_____
! _ _______________________ _ _ _
! real link state: ____| |_| |_| |_| |_| |________
!
! time --->
! client reads: 0 1 0 1 0 1
! ________ _________ __________
! client link state: ____| |____________| |______|
! _ __ _________ _ ___________
! real link state: ____| |_| |______________| |_| |_|
!
! time --->
Examples
~~~~~~~~

View File

@ -63,7 +63,9 @@ Interface_policy::Interface_policy(Genode::Session_label const &label,
_label { label },
_config { config },
_session_env { session_env }
{ }
{
_session_link_state_transition(UP);
}
Domain_name
@ -88,6 +90,153 @@ Net::Session_component::Interface_policy::determine_domain_name() const
}
void Net::Session_component::
Interface_policy::_session_link_state_transition(Transient_link_state tls)
{
_transient_link_state = tls;
Signal_transmitter(_session_link_state_sigh).submit();
}
void Net::Session_component::Interface_policy::interface_unready()
{
switch (_transient_link_state) {
case UP_ACKNOWLEDGED:
_session_link_state_transition(DOWN);
break;
case UP:
_transient_link_state = UP_DOWN;
break;
case DOWN_UP:
_transient_link_state = DOWN_UP_DOWN;
break;
case UP_DOWN:
break;
case UP_DOWN_UP:
_transient_link_state = UP_DOWN;
break;
case DOWN_ACKNOWLEDGED: break;
case DOWN: break;
case DOWN_UP_DOWN: break;
}
}
void Net::Session_component::Interface_policy::interface_ready()
{
switch (_transient_link_state) {
case DOWN_ACKNOWLEDGED:
_session_link_state_transition(UP);
break;
case DOWN:
_transient_link_state = DOWN_UP;
break;
case UP_DOWN:
_transient_link_state = UP_DOWN_UP;
break;
case DOWN_UP:
break;
case DOWN_UP_DOWN:
_transient_link_state = DOWN_UP;
break;
case UP_ACKNOWLEDGED: break;
case UP: break;
case UP_DOWN_UP: break;
}
}
bool
Net::Session_component::Interface_policy::interface_link_state() const
{
switch (_transient_link_state) {
case DOWN_ACKNOWLEDGED: return false;
case DOWN: return false;
case DOWN_UP: return true;
case DOWN_UP_DOWN: return false;
case UP_ACKNOWLEDGED: return true;
case UP: return true;
case UP_DOWN: return false;
case UP_DOWN_UP: return true;
}
class Never_reached : Exception { };
throw Never_reached { };
}
bool
Net::Session_component::Interface_policy::read_and_ack_session_link_state()
{
switch (_transient_link_state) {
case DOWN_ACKNOWLEDGED:
return false;
case DOWN:
_transient_link_state = DOWN_ACKNOWLEDGED;
return false;
case DOWN_UP:
_session_link_state_transition(UP);
return false;
case DOWN_UP_DOWN:
_session_link_state_transition(UP_DOWN);
return false;
case UP_ACKNOWLEDGED:
return true;
case UP:
_transient_link_state = UP_ACKNOWLEDGED;
return true;
case UP_DOWN:
_session_link_state_transition(DOWN);
return true;
case UP_DOWN_UP:
_session_link_state_transition(DOWN_UP);
return true;
}
class Never_reached { };
throw Never_reached { };
}
void Net::Session_component::Interface_policy::
session_link_state_sigh(Signal_context_capability sigh)
{
_session_link_state_sigh = sigh;
}
/***********************
** Session_component **
***********************/
@ -109,7 +258,7 @@ Net::Session_component::Session_component(Session_env &sessio
_interface_policy { label, _session_env, config },
_interface { _session_env.ep(), timer, router_mac, _alloc,
mac, config, interfaces, *_tx.sink(),
*_rx.source(), _link_state, _interface_policy },
*_rx.source(), _interface_policy },
_ram_ds { ram_ds }
{
_interface.attach_to_domain();
@ -121,6 +270,19 @@ Net::Session_component::Session_component(Session_env &sessio
}
bool Net::Session_component::link_state()
{
return _interface_policy.read_and_ack_session_link_state();
}
void Net::
Session_component::link_state_sigh(Signal_context_capability sigh)
{
_interface_policy.session_link_state_sigh(sigh);
}
/**********
** Root **
**********/

View File

@ -257,13 +257,45 @@ class Net::Session_component : private Session_component_base,
{
private:
struct Interface_policy : Net::Interface_policy
class Interface_policy : public Net::Interface_policy
{
private:
using Signal_context_capability =
Genode::Signal_context_capability;
/*
* The transient link state is a combination of session and
* interface link state. The first word in the value name
* denotes the link state of the session. If the session
* link state has already been read by the client and
* can therefore be altered directly, it is marked as
* 'ACKNOWLEDGED'. Otherwise, the denoted session state
* has to stay fixed until the client has read it. In this
* case, the session link state in the value name may be
* followed by the pending link state edges. Consequently,
* the last 'UP' or 'DOWN' in each value name denotes the
* router-internal interface link-state.
*/
enum Transient_link_state
{
DOWN_ACKNOWLEDGED,
DOWN,
DOWN_UP,
DOWN_UP_DOWN,
UP_ACKNOWLEDGED,
UP,
UP_DOWN,
UP_DOWN_UP
};
Genode::Session_label const _label;
Const_reference<Configuration> _config;
Genode::Session_env const &_session_env;
Transient_link_state _transient_link_state { DOWN_ACKNOWLEDGED };
Signal_context_capability _session_link_state_sigh { };
void _session_link_state_transition(Transient_link_state tls);
public:
@ -271,6 +303,10 @@ class Net::Session_component : private Session_component_base,
Genode::Session_env const &session_env,
Configuration const &config);
bool read_and_ack_session_link_state();
void session_link_state_sigh(Genode::Signal_context_capability sigh);
/***************************
** Net::Interface_policy **
@ -280,9 +316,11 @@ class Net::Session_component : private Session_component_base,
void handle_config(Configuration const &config) override { _config = config; }
Genode::Session_label const &label() const override { return _label; }
void report(Genode::Xml_generator &xml) const override { _session_env.report(xml); };
void interface_unready() override;
void interface_ready() override;
bool interface_link_state() const override;
};
bool _link_state { true };
Interface_policy _interface_policy;
Interface _interface;
Genode::Ram_dataspace_capability const _ram_ds;
@ -306,9 +344,8 @@ class Net::Session_component : private Session_component_base,
******************/
Mac_address mac_address() override { return _interface.mac(); }
bool link_state() override { return _interface.link_state(); }
void link_state_sigh(Genode::Signal_context_capability sigh) override {
_interface.session_link_state_sigh(sigh); }
bool link_state() override;
void link_state_sigh(Genode::Signal_context_capability sigh) override;
/***************

View File

@ -318,7 +318,7 @@ Interface::_transport_rules(Domain &local_domain, L3_protocol const prot) const
void Interface::_attach_to_domain_raw(Domain &domain)
{
_domain = domain;
Signal_transmitter(_session_link_state_sigh).submit();
_policy.interface_ready();
_interfaces.remove(this);
domain.attach_interface(*this);
}
@ -330,7 +330,7 @@ void Interface::_detach_from_domain_raw()
domain.detach_interface(*this);
_interfaces.insert(this);
_domain = Pointer<Domain>();
Signal_transmitter(_session_link_state_sigh).submit();
_policy.interface_unready();
domain.tcp_stats().dissolve_interface(_tcp_stats);
domain.udp_stats().dissolve_interface(_udp_stats);
@ -347,7 +347,7 @@ void Interface::_update_domain_object(Domain &new_domain) {
old_domain.interface_updates_domain_object(*this);
_interfaces.insert(this);
_domain = Pointer<Domain>();
Signal_transmitter(_session_link_state_sigh).submit();
_policy.interface_unready();
old_domain.tcp_stats().dissolve_interface(_tcp_stats);
old_domain.udp_stats().dissolve_interface(_udp_stats);
@ -357,7 +357,7 @@ void Interface::_update_domain_object(Domain &new_domain) {
/* attach raw */
_domain = new_domain;
Signal_transmitter(_session_link_state_sigh).submit();
_policy.interface_ready();
_interfaces.remove(this);
new_domain.attach_interface(*this);
}
@ -408,12 +408,6 @@ void Interface::attach_to_ip_config(Domain &domain,
}
void Interface::session_link_state_sigh(Signal_context_capability sigh)
{
_session_link_state_sigh = sigh;
}
void Interface::detach_from_ip_config()
{
/* destroy our own ARP waiters */
@ -440,14 +434,15 @@ void Interface::detach_from_ip_config()
void Interface::detach_from_remote_ip_config()
{
/* only the DNS server address of the local DHCP server can be remote */
Signal_transmitter(_session_link_state_sigh).submit();
_policy.interface_unready();
}
void Interface::attach_to_remote_ip_config()
{
/* only the DNS server address of the local DHCP server can be remote */
Signal_transmitter(_session_link_state_sigh).submit();
_policy.interface_ready();
}
@ -876,11 +871,11 @@ void Interface::_send_icmp_dst_unreachable(Ipv4_address_prefix const &local_intf
bool Interface::link_state() const
{
return _domain.valid() && _session_link_state;
return _policy.interface_link_state();
}
void Interface::handle_link_state()
void Interface::handle_interface_link_state()
{
struct Keep_ip_config : Exception { };
try {
@ -900,7 +895,7 @@ void Interface::handle_link_state()
catch (Keep_ip_config) { }
/* force report if configured */
try { _config().report().handle_link_state(); }
try { _config().report().handle_interface_link_state(); }
catch (Pointer<Report>::Invalid) { }
}
@ -1713,12 +1708,10 @@ Interface::Interface(Genode::Entrypoint &ep,
Interface_list &interfaces,
Packet_stream_sink &sink,
Packet_stream_source &source,
bool &session_link_state,
Interface_policy &policy)
:
_sink { sink },
_source { source },
_session_link_state { session_link_state },
_sink_ack { ep, *this, &Interface::_ack_avail },
_sink_submit { ep, *this, &Interface::_ready_to_submit },
_source_ack { ep, *this, &Interface::_ready_to_ack },

View File

@ -92,6 +92,12 @@ struct Net::Interface_policy
virtual Genode::Session_label const &label() const = 0;
virtual void interface_ready() = 0;
virtual void interface_unready() = 0;
virtual bool interface_link_state() const = 0;
virtual void report(Genode::Xml_generator &) const { throw Report::Empty(); }
virtual ~Interface_policy() { }
@ -129,8 +135,6 @@ class Net::Interface : private Interface_list::Element
Packet_stream_sink &_sink;
Packet_stream_source &_source;
bool &_session_link_state;
Signal_context_capability _session_link_state_sigh { };
Signal_handler _sink_ack;
Signal_handler _sink_submit;
Signal_handler _source_ack;
@ -351,6 +355,8 @@ class Net::Interface : private Interface_list::Element
Ipv4_packet const &req_ip,
Icmp_packet::Code const code);
bool link_state() const;
/***********************************
** Packet-stream signal handlers **
@ -386,7 +392,6 @@ class Net::Interface : private Interface_list::Element
Interface_list &interfaces,
Packet_stream_sink &sink,
Packet_stream_source &source,
bool &session_link_state,
Interface_policy &policy);
virtual ~Interface();
@ -442,9 +447,7 @@ class Net::Interface : private Interface_list::Element
void attach_to_domain_finish();
bool link_state() const;
void handle_link_state();
void handle_interface_link_state();
void report(Genode::Xml_generator &xml);
@ -467,8 +470,6 @@ class Net::Interface : private Interface_list::Element
Interface_link_stats &icmp_stats() { return _icmp_stats; }
Interface_object_stats &arp_stats() { return _arp_stats; }
Interface_object_stats &dhcp_stats() { return _dhcp_stats; }
void session_link_state_sigh(Genode::Signal_context_capability sigh);
};
#endif /* _INTERFACE_H_ */

View File

@ -90,7 +90,7 @@ void Net::Report::handle_config()
_report();
}
void Net::Report::handle_link_state()
void Net::Report::handle_interface_link_state()
{
if (!_link_state_triggers) {
return; }

View File

@ -75,7 +75,7 @@ class Net::Report
void handle_config();
void handle_link_state();
void handle_interface_link_state();
/***************

View File

@ -112,13 +112,33 @@ void Net::Uplink::print(Output &output) const
***************************/
Net::Uplink_interface_base::Uplink_interface_base(Domain_name const &domain_name,
Session_label const &label)
Session_label const &label,
bool const &session_link_state)
:
_domain_name { domain_name },
_label { label }
_domain_name { domain_name },
_label { label },
_session_link_state { session_link_state }
{ }
void Net::Uplink_interface_base::interface_unready()
{
_interface_ready = false;
};
void Net::Uplink_interface_base::interface_ready()
{
_interface_ready = true;
};
bool Net::Uplink_interface_base::interface_link_state() const
{
return _interface_ready && _session_link_state;
}
/**********************
** Uplink_interface **
**********************/
@ -131,14 +151,14 @@ Net::Uplink_interface::Uplink_interface(Env &env,
Domain_name const &domain_name,
Session_label const &label)
:
Uplink_interface_base { domain_name, label },
Nic::Packet_allocator { &alloc },
Nic::Connection { env, this, BUF_SIZE, BUF_SIZE, label.string() },
_link_state_handler { env.ep(), *this,
&Uplink_interface::_handle_link_state },
_interface { env.ep(), timer, mac_address(), alloc,
Mac_address(), config, interfaces, *rx(), *tx(),
_link_state, *this }
Uplink_interface_base { domain_name, label, _session_link_state },
Nic::Packet_allocator { &alloc },
Nic::Connection { env, this, BUF_SIZE, BUF_SIZE, label.string() },
_session_link_state_handler { env.ep(), *this,
&Uplink_interface::_handle_session_link_state },
_interface { env.ep(), timer, mac_address(), alloc,
Mac_address(), config, interfaces, *rx(), *tx(),
*this }
{
/* install packet stream signal handlers */
rx_channel()->sigh_ready_to_ack (_interface.sink_ack());
@ -147,13 +167,13 @@ Net::Uplink_interface::Uplink_interface(Env &env,
tx_channel()->sigh_ready_to_submit(_interface.source_submit());
/* initialize link state handling */
Nic::Connection::link_state_sigh(_link_state_handler);
_link_state = link_state();
Nic::Connection::link_state_sigh(_session_link_state_handler);
_session_link_state = Nic::Connection::link_state();
}
void Net::Uplink_interface::_handle_link_state()
void Net::Uplink_interface::_handle_session_link_state()
{
_link_state = link_state();
_interface.handle_link_state();
_session_link_state = Nic::Connection::link_state();
_interface.handle_interface_link_state();
}

View File

@ -104,8 +104,10 @@ class Net::Uplink_interface_base : public Interface_policy
{
private:
Const_reference<Domain_name> _domain_name;
Genode::Session_label const _label;
Const_reference<Domain_name> _domain_name;
Genode::Session_label const _label;
bool const &_session_link_state;
bool _interface_ready { false };
/***************************
@ -115,11 +117,15 @@ class Net::Uplink_interface_base : public Interface_policy
Domain_name determine_domain_name() const override { return _domain_name(); };
void handle_config(Configuration const &) override { }
Genode::Session_label const &label() const override { return _label; }
void interface_unready() override;
void interface_ready() override;
bool interface_link_state() const override;
public:
Uplink_interface_base(Domain_name const &domain_name,
Genode::Session_label const &label);
Genode::Session_label const &label,
bool const &session_link_state);
virtual ~Uplink_interface_base() { }
@ -143,13 +149,13 @@ class Net::Uplink_interface : public Uplink_interface_base,
BUF_SIZE = Nic::Session::QUEUE_SIZE * PKT_SIZE,
};
bool _link_state { false };
Genode::Signal_handler<Uplink_interface> _link_state_handler;
bool _session_link_state { false };
Genode::Signal_handler<Uplink_interface> _session_link_state_handler;
Net::Interface _interface;
Ipv4_address_prefix _read_interface();
void _handle_link_state();
void _handle_session_link_state();
public: