From 2f32308c0bba2e49f134bfecf32a4fe7af396608 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Josef=20S=C3=B6ntgen?= Date: Thu, 2 Nov 2017 14:53:48 +0100 Subject: [PATCH] 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. --- repos/dde_linux/src/lib/wifi/nic.cc | 58 ++++++++++++++++++++++++----- 1 file changed, 49 insertions(+), 9 deletions(-) diff --git a/repos/dde_linux/src/lib/wifi/nic.cc b/repos/dde_linux/src/lib/wifi/nic.cc index 441c3ec9c9..42fd30934b 100644 --- a/repos/dde_linux/src/lib/wifi/nic.cc +++ b/repos/dde_linux/src/lib/wifi/nic.cc @@ -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(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");