mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-15 08:14:15 +08:00
20a19d1df3
Create a hold_txs atomic variable and increment/decrement it when relevant, ie. when we want to hold the queue or release it: currently all the "stopped" situations are suitable, but very soon we will more extensively use this feature for MLME purposes. Upon release, the atomic counter is decremented and checked. If it is back to 0, then the netif queue gets woken up. This makes the whole process fully transparent, provided that all the users of ieee802154_wake/stop_queue() now call ieee802154_hold/release_queue() instead. In no situation individual drivers should call any of these helpers manually in order to avoid messing with the counters. There are other functions more suited for this purpose which have been introduced, such as the _xmit_complete() and _xmit_error() helpers which will handle all that for them. One advantage is that, as no more drivers call the stop/wake helpers directly, we can safely stop exporting them and only declare the hold/release ones in a header only accessible to the core. Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com> Acked-by: Alexander Aring <aahringo@redhat.com> Link: https://lore.kernel.org/r/20220519150516.443078-6-miquel.raynal@bootlin.com Signed-off-by: Stefan Schmidt <stefan@datenfreihafen.org>
157 lines
4.2 KiB
C
157 lines
4.2 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
/*
|
|
*
|
|
* Authors:
|
|
* Alexander Aring <aar@pengutronix.de>
|
|
*
|
|
* Based on: net/mac80211/util.c
|
|
*/
|
|
|
|
#include "ieee802154_i.h"
|
|
#include "driver-ops.h"
|
|
|
|
/* privid for wpan_phys to determine whether they belong to us or not */
|
|
const void *const mac802154_wpan_phy_privid = &mac802154_wpan_phy_privid;
|
|
|
|
/**
|
|
* ieee802154_wake_queue - wake ieee802154 queue
|
|
* @local: main mac object
|
|
*
|
|
* Tranceivers usually have either one transmit framebuffer or one framebuffer
|
|
* for both transmitting and receiving. Hence, the core currently only handles
|
|
* one frame at a time for each phy, which means we had to stop the queue to
|
|
* avoid new skb to come during the transmission. The queue then needs to be
|
|
* woken up after the operation.
|
|
*/
|
|
static void ieee802154_wake_queue(struct ieee802154_hw *hw)
|
|
{
|
|
struct ieee802154_local *local = hw_to_local(hw);
|
|
struct ieee802154_sub_if_data *sdata;
|
|
|
|
rcu_read_lock();
|
|
list_for_each_entry_rcu(sdata, &local->interfaces, list) {
|
|
if (!sdata->dev)
|
|
continue;
|
|
|
|
netif_wake_queue(sdata->dev);
|
|
}
|
|
rcu_read_unlock();
|
|
}
|
|
|
|
/**
|
|
* ieee802154_stop_queue - stop ieee802154 queue
|
|
* @local: main mac object
|
|
*
|
|
* Tranceivers usually have either one transmit framebuffer or one framebuffer
|
|
* for both transmitting and receiving. Hence, the core currently only handles
|
|
* one frame at a time for each phy, which means we need to tell upper layers to
|
|
* stop giving us new skbs while we are busy with the transmitted one. The queue
|
|
* must then be stopped before transmitting.
|
|
*/
|
|
static void ieee802154_stop_queue(struct ieee802154_hw *hw)
|
|
{
|
|
struct ieee802154_local *local = hw_to_local(hw);
|
|
struct ieee802154_sub_if_data *sdata;
|
|
|
|
rcu_read_lock();
|
|
list_for_each_entry_rcu(sdata, &local->interfaces, list) {
|
|
if (!sdata->dev)
|
|
continue;
|
|
|
|
netif_stop_queue(sdata->dev);
|
|
}
|
|
rcu_read_unlock();
|
|
}
|
|
|
|
void ieee802154_hold_queue(struct ieee802154_local *local)
|
|
{
|
|
unsigned long flags;
|
|
|
|
spin_lock_irqsave(&local->phy->queue_lock, flags);
|
|
if (!atomic_fetch_inc(&local->phy->hold_txs))
|
|
ieee802154_stop_queue(&local->hw);
|
|
spin_unlock_irqrestore(&local->phy->queue_lock, flags);
|
|
}
|
|
|
|
void ieee802154_release_queue(struct ieee802154_local *local)
|
|
{
|
|
unsigned long flags;
|
|
|
|
spin_lock_irqsave(&local->phy->queue_lock, flags);
|
|
if (!atomic_dec_and_test(&local->phy->hold_txs))
|
|
ieee802154_wake_queue(&local->hw);
|
|
spin_unlock_irqrestore(&local->phy->queue_lock, flags);
|
|
}
|
|
|
|
enum hrtimer_restart ieee802154_xmit_ifs_timer(struct hrtimer *timer)
|
|
{
|
|
struct ieee802154_local *local =
|
|
container_of(timer, struct ieee802154_local, ifs_timer);
|
|
|
|
ieee802154_release_queue(local);
|
|
|
|
return HRTIMER_NORESTART;
|
|
}
|
|
|
|
void ieee802154_xmit_complete(struct ieee802154_hw *hw, struct sk_buff *skb,
|
|
bool ifs_handling)
|
|
{
|
|
struct ieee802154_local *local = hw_to_local(hw);
|
|
|
|
local->tx_result = IEEE802154_SUCCESS;
|
|
|
|
if (ifs_handling) {
|
|
u8 max_sifs_size;
|
|
|
|
/* If transceiver sets CRC on his own we need to use lifs
|
|
* threshold len above 16 otherwise 18, because it's not
|
|
* part of skb->len.
|
|
*/
|
|
if (hw->flags & IEEE802154_HW_TX_OMIT_CKSUM)
|
|
max_sifs_size = IEEE802154_MAX_SIFS_FRAME_SIZE -
|
|
IEEE802154_FCS_LEN;
|
|
else
|
|
max_sifs_size = IEEE802154_MAX_SIFS_FRAME_SIZE;
|
|
|
|
if (skb->len > max_sifs_size)
|
|
hrtimer_start(&local->ifs_timer,
|
|
hw->phy->lifs_period * NSEC_PER_USEC,
|
|
HRTIMER_MODE_REL);
|
|
else
|
|
hrtimer_start(&local->ifs_timer,
|
|
hw->phy->sifs_period * NSEC_PER_USEC,
|
|
HRTIMER_MODE_REL);
|
|
} else {
|
|
ieee802154_release_queue(local);
|
|
}
|
|
|
|
dev_consume_skb_any(skb);
|
|
atomic_dec(&hw->phy->ongoing_txs);
|
|
}
|
|
EXPORT_SYMBOL(ieee802154_xmit_complete);
|
|
|
|
void ieee802154_xmit_error(struct ieee802154_hw *hw, struct sk_buff *skb,
|
|
int reason)
|
|
{
|
|
struct ieee802154_local *local = hw_to_local(hw);
|
|
|
|
local->tx_result = reason;
|
|
ieee802154_release_queue(local);
|
|
dev_kfree_skb_any(skb);
|
|
atomic_dec(&hw->phy->ongoing_txs);
|
|
}
|
|
EXPORT_SYMBOL(ieee802154_xmit_error);
|
|
|
|
void ieee802154_xmit_hw_error(struct ieee802154_hw *hw, struct sk_buff *skb)
|
|
{
|
|
ieee802154_xmit_error(hw, skb, IEEE802154_SYSTEM_ERROR);
|
|
}
|
|
EXPORT_SYMBOL(ieee802154_xmit_hw_error);
|
|
|
|
void ieee802154_stop_device(struct ieee802154_local *local)
|
|
{
|
|
flush_workqueue(local->workqueue);
|
|
hrtimer_cancel(&local->ifs_timer);
|
|
drv_stop(local);
|
|
}
|