diff --git a/repos/os/src/server/nic_router/interface.cc b/repos/os/src/server/nic_router/interface.cc index d859a004b9..18bb944b09 100644 --- a/repos/os/src/server/nic_router/interface.cc +++ b/repos/os/src/server/nic_router/interface.cc @@ -24,6 +24,7 @@ #include #include #include +#include using namespace Net; using Genode::Deallocator; @@ -98,10 +99,10 @@ static void _destroy_links(Link_list &links, template -static void _destroy_some_links(Link_list &links, - Link_list &dissolved_links, - Deallocator &dealloc, - unsigned long &max) +static void _early_drop_links(Link_list &links, + Link_list &dissolved_links, + Deallocator &dealloc, + unsigned long &max) { if (!max) { return; } @@ -112,10 +113,15 @@ static void _destroy_some_links(Link_list &links, if (!--max) { return; } } - while (Link *link = links.first()) { - _destroy_link(*link, links, dealloc); - if (!--max) { - return; } + Link *link_ptr = links.first(); + while (link_ptr) { + Link *next_ptr = link_ptr->next(); + if (static_cast(link_ptr)->can_early_drop()) { + _destroy_link(*link_ptr, links, dealloc); + if (!--max) { + return; } + } + link_ptr = next_ptr; } } @@ -493,6 +499,15 @@ void Interface::_detach_from_domain() } +void Interface::_try_emergency_free_quota() +{ + unsigned long max = MAX_FREE_OPS_PER_EMERGENCY; + _early_drop_links(_icmp_links, _dissolved_icmp_links, _alloc, max); + _early_drop_links (_udp_links, _dissolved_udp_links, _alloc, max); + _early_drop_links (_tcp_links, _dissolved_tcp_links, _alloc, max); +} + + Packet_result Interface::_new_link(L3_protocol const protocol, Domain &local_domain, Link_side_id const &local, @@ -503,49 +518,37 @@ Packet_result Interface::_new_link(L3_protocol const protocol, Packet_result result { }; switch (protocol) { case L3_protocol::TCP: - try { - new (_alloc) + retry_once( + [&] { + new (_alloc) Tcp_link { *this, local_domain, local, remote_port_alloc_ptr, remote_domain, - remote, _timer, _config(), protocol, _tcp_stats }; - } - catch (Out_of_ram) { - _tcp_stats.refused_for_ram++; - result = packet_drop("out of RAM while creating TCP link"); - } - catch (Out_of_caps) { - _tcp_stats.refused_for_ram++; - result = packet_drop("out of CAPs while creating TCP link"); - } + remote, _timer, _config(), protocol, _tcp_stats }; }, + [&] { _try_emergency_free_quota(); }, + [&] { + _tcp_stats.refused_for_ram++; + result = packet_drop("out of quota while creating TCP link"); }); break; case L3_protocol::UDP: - try { - new (_alloc) - Udp_link { *this, local_domain, local, remote_port_alloc_ptr, remote_domain, - remote, _timer, _config(), protocol, _udp_stats }; - } - catch (Out_of_ram) { - _udp_stats.refused_for_ram++; - result = packet_drop("out of RAM while creating UDP link"); - } - catch (Out_of_caps) { - _udp_stats.refused_for_ram++; - result = packet_drop("out of CAPs while creating UDP link"); - } + retry_once( + [&] { + new (_alloc) + Udp_link { *this, local_domain, local, remote_port_alloc_ptr, remote_domain, + remote, _timer, _config(), protocol, _udp_stats }; }, + [&] { _try_emergency_free_quota(); }, + [&] { + _udp_stats.refused_for_ram++; + result = packet_drop("out of quota while creating UDP link"); }); break; case L3_protocol::ICMP: - try { - new (_alloc) - Icmp_link { *this, local_domain, local, remote_port_alloc_ptr, remote_domain, - remote, _timer, _config(), protocol, _icmp_stats }; - } - catch (Out_of_ram) { - _icmp_stats.refused_for_ram++; - result = packet_drop("out of RAM while creating ICMP link"); - } - catch (Out_of_caps) { - _icmp_stats.refused_for_ram++; - result = packet_drop("out of CAPs while creating ICMP link"); - } + retry_once( + [&] { + new (_alloc) + Icmp_link { *this, local_domain, local, remote_port_alloc_ptr, remote_domain, + remote, _timer, _config(), protocol, _icmp_stats }; }, + [&] { _try_emergency_free_quota(); }, + [&] { + _icmp_stats.refused_for_ram++; + result = packet_drop("out of quota while creating ICMP link"); }); break; default: ASSERT_NEVER_REACHED; } return result; @@ -606,12 +609,13 @@ Packet_result Interface::_adapt_eth(Ethernet_frame ð, interface._broadcast_arp_request( remote_ip_cfg.interface().address, hop_ip); }); - try { - new (_alloc) Arp_waiter { *this, remote_domain, hop_ip, pkt }; - result = packet_postponed(); - } - catch (Out_of_ram) { result = packet_drop("out of RAM while creating ARP waiter"); } - catch (Out_of_caps) { result = packet_drop("out of CAPs while creating ARP waiter"); } + retry_once( + [&] { + new (_alloc) Arp_waiter { *this, remote_domain, hop_ip, pkt }; + result = packet_postponed(); + }, + [&] { _try_emergency_free_quota(); }, + [&] { result = packet_drop("out of quota while creating ARP waiter"); }); } ); }; @@ -774,26 +778,31 @@ Packet_result Interface::_new_dhcp_allocation(Ethernet_frame ð, Domain &local_domain) { Packet_result result { }; - auto ok_fn = [&] (Ipv4_address const &ip) { - Dhcp_allocation &allocation = *new (_alloc) - Dhcp_allocation { *this, ip, dhcp.client_mac(), - _timer, _config().dhcp_offer_timeout() }; + dhcp_srv.alloc_ip().with_result( + [&] (Ipv4_address const &ip) { + retry_once( + [&] { + Dhcp_allocation &allocation = *new (_alloc) + Dhcp_allocation { *this, ip, dhcp.client_mac(), + _timer, _config().dhcp_offer_timeout() }; - _dhcp_allocations.insert(allocation); - if (_config().verbose()) { - log("[", local_domain, "] offer DHCP allocation: ", allocation); } + _dhcp_allocations.insert(allocation); + if (_config().verbose()) { + log("[", local_domain, "] offer DHCP allocation: ", allocation); } - _send_dhcp_reply(dhcp_srv, eth.src(), dhcp.client_mac(), - allocation.ip(), - Dhcp_packet::Message_type::OFFER, - dhcp.xid(), - local_domain.ip_config().interface()); + _send_dhcp_reply(dhcp_srv, eth.src(), dhcp.client_mac(), + allocation.ip(), + Dhcp_packet::Message_type::OFFER, + dhcp.xid(), + local_domain.ip_config().interface()); + + result = packet_handled(); + }, + [&] { _try_emergency_free_quota(); }, + [&] { result = packet_drop("out of quota while creating DHCP allocation"); }); + }, + [&] (auto) { result = packet_drop("failed to allocate IP for DHCP client"); }); - result = packet_handled(); - }; - try { dhcp_srv.alloc_ip().with_result(ok_fn, [&] (auto) { result = packet_drop("failed to allocate IP for DHCP client"); }); } - catch (Out_of_ram) { result = packet_drop("out of RAM while creating DHCP allocation"); } - catch (Out_of_caps) { result = packet_drop("out of CAPs while creating DHCP allocation"); } return result; } diff --git a/repos/os/src/server/nic_router/interface.h b/repos/os/src/server/nic_router/interface.h index beea19c9a3..75f13aff8c 100644 --- a/repos/os/src/server/nic_router/interface.h +++ b/repos/os/src/server/nic_router/interface.h @@ -128,7 +128,7 @@ class Net::Interface : private Interface_list::Element using Signal_context_capability = Genode::Signal_context_capability; enum { IPV4_TIME_TO_LIVE = 64 }; - enum { MAX_FREE_OPS_PER_EMERGENCY = 1024 }; + enum { MAX_FREE_OPS_PER_EMERGENCY = 100 }; struct Update_domain { @@ -193,6 +193,8 @@ class Net::Interface : private Interface_list::Element void _release_dhcp_allocation(Dhcp_allocation &allocation, Domain &local_domain); + void _try_emergency_free_quota(); + [[nodiscard]] Packet_result _new_dhcp_allocation(Ethernet_frame ð, Dhcp_packet &dhcp, Dhcp_server &dhcp_srv, diff --git a/repos/os/src/server/nic_router/link.cc b/repos/os/src/server/nic_router/link.cc index 68930ea87f..58e84991b4 100644 --- a/repos/os/src/server/nic_router/link.cc +++ b/repos/os/src/server/nic_router/link.cc @@ -253,7 +253,7 @@ void Tcp_link::_tcp_packet(Tcp_packet &tcp, } } } - if (_state == State::OPEN) { + if (_state == State::OPENING || _state == State::OPEN) { _packet(); } else { _dissolve_timeout.schedule( @@ -266,6 +266,7 @@ void Tcp_link::server_packet(Tcp_packet &tcp) { if (_opening) { _opening = false; + _state = State::OPEN; _stats_curr()--; if (&_stats_curr() == &_stats.opening) { _stats_curr = _stats.open; } _stats_curr()++; diff --git a/repos/os/src/server/nic_router/link.h b/repos/os/src/server/nic_router/link.h index 45c8681d06..54ff937e69 100644 --- a/repos/os/src/server/nic_router/link.h +++ b/repos/os/src/server/nic_router/link.h @@ -256,7 +256,7 @@ class Net::Tcp_link : public Link { private: - enum class State : Genode::uint8_t { OPEN, CLOSING, CLOSED, }; + enum class State : Genode::uint8_t { OPENING, OPEN, CLOSING, CLOSED }; struct Peer { @@ -264,7 +264,7 @@ class Net::Tcp_link : public Link bool fin_acked { false }; }; - State _state { State::OPEN }; + State _state { State::OPENING }; Peer _client { }; Peer _server { }; @@ -292,6 +292,8 @@ class Net::Tcp_link : public Link void client_packet(Tcp_packet &tcp) { _tcp_packet(tcp, _client, _server); } void server_packet(Tcp_packet &tcp); + + bool can_early_drop() { return _state != State::OPEN; } }; @@ -311,6 +313,8 @@ struct Net::Udp_link : Link void client_packet() { _packet(); } void server_packet(); + + bool can_early_drop() { return true; } }; @@ -330,6 +334,8 @@ struct Net::Icmp_link : Link void client_packet() { _packet(); } void server_packet(); + + bool can_early_drop() { return true; } }; #endif /* _LINK_H_ */ diff --git a/repos/os/src/server/nic_router/retry.h b/repos/os/src/server/nic_router/retry.h new file mode 100644 index 0000000000..435943ea29 --- /dev/null +++ b/repos/os/src/server/nic_router/retry.h @@ -0,0 +1,38 @@ +/* + * \brief Utility to execute a function repeatedly + * \author Martin Stein + * \date 2015-04-29 + */ + +/* + * Copyright (C) 2015-2017 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +#ifndef _RETRY_H_ +#define _RETRY_H_ + +namespace Net { + + template + void retry_once(auto const &attempt_fn, auto const &exception_fn, auto const &failed_fn) + { + for (unsigned i = 0; ; i++) { + try { + attempt_fn(); + return; + } + catch (EXCEPTION_1) { } + catch (EXCEPTION_2) { } + if (i == 1) { + failed_fn(); + return; + } + exception_fn(); + } + } +} + +#endif /* _RETRY_H_ */