nic_router: packet allocation w/o exceptions

In overload situations, i.e. when a sender fills up the entire buffer, we land
in situations where the sender receives an ack_avail signal, releases one
packet, allocates and sends a packet and fails to allocate a second packet.
This is especially relevant if the receiver does not batch ack_avail signals
(such as vfs_lwip). In those ping-pong scheduling scenarios, the overhead from
catching the Packet_alloc_failed exception becomes significant. In case of the
NIC router, we will land in an overload situation if the sender is faster than
the receiver. The packet buffer will be filled up at some point and the NIC
router starts to drop packets. For every dropped packet, we currently have to
catch the Packet_alloc_failed exception.

This commit adds a new method alloc_packet_attempt to Packet_stream_source that
has almost the same signature as the older alloc_packet method but returns
an Attempt<Packet_descriptor, Alloc_packet_error> object. As the method already
used the allocator back end exception-less, changes on lower levels were not
needed. Furthermore, the NIC router was modified to use the new exception-less
alloc_packet_attempt instead of alloc_packet.

Ref #4555
This commit is contained in:
Martin Stein 2022-07-21 15:43:58 +02:00 committed by Christian Helmuth
parent 3f69457a94
commit e32157e21b
3 changed files with 40 additions and 25 deletions

View File

@ -639,6 +639,10 @@ class Genode::Packet_stream_source : private Packet_stream_base
class Saturated_submit_queue : Exception { };
class Empty_ack_queue : Exception { };
enum class Alloc_packet_error { FAILED };
using Alloc_packet_result = Attempt<Packet_descriptor, Alloc_packet_error>;
/**
* Constructor
*
@ -726,6 +730,29 @@ class Genode::Packet_stream_source : private Packet_stream_base
throw Packet_alloc_failed(); });
}
/**
* Allocate packet without throwing exceptions
*
* \param size size of packet in bytes
* \param align alignment of packet as log2 value, default is 1 byte
* \return an Attempt object that either contains an error or a
* packet descriptor with an assigned range within the
* bulk buffer shared between source and sink
*/
Alloc_packet_result alloc_packet_attempt(Genode::size_t size, unsigned align = PACKET_ALIGNMENT)
{
if (size == 0)
return Packet_descriptor(0, 0);
return _packet_alloc.alloc_aligned(size, align).convert<Alloc_packet_result>(
[&] (void *base) {
return Packet_descriptor((Genode::off_t)base, size); },
[&] (Allocator::Alloc_error) {
return Alloc_packet_error::FAILED; });
}
/**
* Get pointer to the content of the specified packet
*

View File

@ -1850,15 +1850,6 @@ void Interface::send(Ethernet_frame &eth,
}
void Interface::_send_alloc_pkt(Packet_descriptor &pkt,
void * &pkt_base,
size_t pkt_size)
{
pkt = _source.alloc_packet(pkt_size);
pkt_base = _source.packet_content(pkt);
}
void Interface::_send_submit_pkt(Packet_descriptor &pkt,
void * &pkt_base,
size_t pkt_size)

View File

@ -307,10 +307,6 @@ class Net::Interface : private Interface_list::Element
void _ack_packet(Packet_descriptor const &pkt);
void _send_alloc_pkt(Genode::Packet_descriptor &pkt,
void * &pkt_base,
Genode::size_t pkt_size);
void _send_submit_pkt(Genode::Packet_descriptor &pkt,
void * &pkt_base,
Genode::size_t pkt_size);
@ -402,18 +398,19 @@ class Net::Interface : private Interface_list::Element
_failed_to_send_packet_submit();
return;
}
try {
Packet_descriptor pkt;
void *pkt_base;
_send_alloc_pkt(pkt, pkt_base, pkt_size);
_source.alloc_packet_attempt(pkt_size).with_result(
[&] (Packet_descriptor pkt)
{
void *pkt_base { _source.packet_content(pkt) };
Size_guard size_guard(pkt_size);
write_to_pkt(pkt_base, size_guard);
_send_submit_pkt(pkt, pkt_base, pkt_size);
}
catch (Packet_stream_source::Packet_alloc_failed) {
},
[&] (Packet_stream_source::Alloc_packet_error)
{
_failed_to_send_packet_alloc();
}
);
}
void send(Ethernet_frame &eth,