wifi_drv: use task for sending frames

Until now the client called the Linux code directly through the EP
when sending ethernet frames and was not part of the driver's internal
task scheduling. This will lead to problems if the sending code needs
to grab a lock as those depend on running from within a Lx::Task.
Although this has only happend recently when using 8260 devices, this
is an issue that needs to be fix.

This commit addresses the issue by using a dedicated transmit task
in whose context the Linux code sends the ethernet frame or rather
newly allocated skb.

Fixes #2559.
This commit is contained in:
Josef Söntgen 2017-11-02 14:53:48 +01:00 committed by Christian Helmuth
parent bb95121ee5
commit 2f32308c0b
1 changed files with 49 additions and 9 deletions

View File

@ -48,21 +48,50 @@ class Wifi_session_component : public Nic::Session_component
net_device *_ndev;
bool _has_link = !(_ndev->state & 1UL << __LINK_STATE_NOCARRIER);
struct Tx_data
{
net_device *ndev;
struct sk_buff *skb;
} _tx_data;
static void _run_tx_task(void *args)
{
Tx_data *data = static_cast<Tx_data*>(args);
while (1) {
Lx::scheduler().current()->block_and_schedule();
net_device *ndev = data->ndev;
struct sk_buff *skb = data->skb;
ndev->netdev_ops->ndo_start_xmit(skb, ndev);
}
}
Lx::Task _tx_task { _run_tx_task, &_tx_data, "tx_task",
Lx::Task::PRIORITY_1, Lx::scheduler() };
protected:
bool _send()
{
using namespace Genode;
if (!_tx.sink()->ready_to_ack())
/*
* We must not be called from another task, just from the
* packet stream dispatcher.
*/
if (Lx::scheduler().active()) {
warning("scheduler active");
return false;
}
if (!_tx.sink()->packet_avail())
return false;
if (!_tx.sink()->ready_to_ack()) { return false; }
if (!_tx.sink()->packet_avail()) { return false; }
Packet_descriptor packet = _tx.sink()->get_packet();
if (!packet.size()) {
Genode::warning("invalid tx packet");
warning("invalid tx packet");
return true;
}
@ -71,18 +100,29 @@ class Wifi_session_component : public Nic::Session_component
unsigned char *data = lxc_skb_put(skb, packet.size());
Genode::memcpy(data, _tx.sink()->packet_content(packet), packet.size());
_ndev->netdev_ops->ndo_start_xmit(skb, _ndev);
_tx_data.ndev = _ndev;
_tx_data.skb = skb;
_tx_task.unblock();
Lx::scheduler().schedule();
_tx.sink()->acknowledge_packet(packet);
return true;
}
void _handle_packet_stream()
void _handle_rx()
{
while (_rx.source()->ack_avail())
while (_rx.source()->ack_avail()) {
_rx.source()->release_packet(_rx.source()->get_acked_packet());
}
}
while (_send()) ;
void _handle_packet_stream() override
{
_handle_rx();
while (_send()) { continue; }
}
public:
@ -117,7 +157,7 @@ class Wifi_session_component : public Nic::Session_component
void receive(struct sk_buff *skb)
{
_handle_packet_stream();
_handle_rx();
if (!_rx.source()->ready_to_submit()) {
Genode::warning("not ready to receive packet");