LinuxKPI: 802.11: basic implementation of *queue(s)/*txq*

Very basic implementations of ieee80211_{wake,stop}_queue[s],
as well as ieee80211_txq_schedule_start(), ieee80211_next_txq(),
and ieee80211_schedule_txq().
Various combinations of these are used by different wireless
drivers, incl. iwlwifi.

Sponsored by:	The FreeBSD Foundation (parts of this work)
MFC after:	3 days
This commit is contained in:
Bjoern A. Zeeb 2023-01-31 16:17:14 +00:00
parent a839757109
commit 5a9a0d7803
3 changed files with 299 additions and 59 deletions

View File

@ -1030,6 +1030,14 @@ struct sk_buff *linuxkpi_ieee80211_probereq_get(struct ieee80211_hw *,
void linuxkpi_ieee80211_tx_status(struct ieee80211_hw *, struct sk_buff *);
void linuxkpi_ieee80211_tx_status_ext(struct ieee80211_hw *,
struct ieee80211_tx_status *);
void linuxkpi_ieee80211_stop_queues(struct ieee80211_hw *);
void linuxkpi_ieee80211_wake_queues(struct ieee80211_hw *);
void linuxkpi_ieee80211_stop_queue(struct ieee80211_hw *, int);
void linuxkpi_ieee80211_wake_queue(struct ieee80211_hw *, int);
void linuxkpi_ieee80211_txq_schedule_start(struct ieee80211_hw *, uint8_t);
struct ieee80211_txq *linuxkpi_ieee80211_next_txq(struct ieee80211_hw *, uint8_t);
void linuxkpi_ieee80211_schedule_txq(struct ieee80211_hw *,
struct ieee80211_txq *, bool);
/* -------------------------------------------------------------------------- */
@ -1504,6 +1512,63 @@ ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb)
/* -------------------------------------------------------------------------- */
static inline void
ieee80211_stop_queues(struct ieee80211_hw *hw)
{
linuxkpi_ieee80211_stop_queues(hw);
}
static inline void
ieee80211_wake_queues(struct ieee80211_hw *hw)
{
linuxkpi_ieee80211_wake_queues(hw);
}
static inline void
ieee80211_stop_queue(struct ieee80211_hw *hw, int qnum)
{
linuxkpi_ieee80211_stop_queue(hw, qnum);
}
static inline void
ieee80211_wake_queue(struct ieee80211_hw *hw, int qnum)
{
linuxkpi_ieee80211_wake_queue(hw, qnum);
}
static inline void
ieee80211_schedule_txq(struct ieee80211_hw *hw, struct ieee80211_txq *txq)
{
linuxkpi_ieee80211_schedule_txq(hw, txq, true);
}
static inline void
ieee80211_return_txq(struct ieee80211_hw *hw, struct ieee80211_txq *txq,
bool withoutpkts)
{
linuxkpi_ieee80211_schedule_txq(hw, txq, true);
}
static inline void
ieee80211_txq_schedule_start(struct ieee80211_hw *hw, uint8_t ac)
{
linuxkpi_ieee80211_txq_schedule_start(hw, ac);
}
static inline void
ieee80211_txq_schedule_end(struct ieee80211_hw *hw, uint8_t ac)
{
/* DO_NADA; */
}
static inline struct ieee80211_txq *
ieee80211_next_txq(struct ieee80211_hw *hw, uint8_t ac)
{
return (linuxkpi_ieee80211_next_txq(hw, ac));
}
/* -------------------------------------------------------------------------- */
static __inline uint8_t
ieee80211_get_tid(struct ieee80211_hdr *hdr)
{
@ -1815,18 +1880,6 @@ ieee80211_tdls_oper_request(struct ieee80211_vif *vif, uint8_t *addr,
TODO();
}
static __inline void
ieee80211_stop_queues(struct ieee80211_hw *hw)
{
TODO();
}
static __inline void
ieee80211_wake_queues(struct ieee80211_hw *hw)
{
TODO();
}
static __inline void
wiphy_rfkill_set_hw_state(struct wiphy *wiphy, bool state)
{
@ -2113,18 +2166,6 @@ ieee80211_queue_work(struct ieee80211_hw *hw, struct work_struct *w)
linuxkpi_ieee80211_queue_work(hw, w);
}
static __inline void
ieee80211_stop_queue(struct ieee80211_hw *hw, uint16_t q)
{
TODO();
}
static __inline void
ieee80211_wake_queue(struct ieee80211_hw *hw, uint16_t q)
{
TODO();
}
static __inline void
ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
{
@ -2255,41 +2296,6 @@ ieee80211_sta_register_airtime(struct ieee80211_sta *sta,
TODO();
}
static __inline void
ieee80211_txq_schedule_start(struct ieee80211_hw *hw, uint8_t ac)
{
TODO();
}
static __inline void
ieee80211_txq_schedule_end(struct ieee80211_hw *hw, uint8_t ac)
{
/* DO_NADA; */
}
static __inline struct ieee80211_txq *
ieee80211_next_txq(struct ieee80211_hw *hw, uint8_t ac)
{
TODO();
return (NULL);
}
static __inline void
ieee80211_schedule_txq(struct ieee80211_hw *hw, struct ieee80211_txq *txq)
{
TODO();
}
static __inline void
ieee80211_return_txq(struct ieee80211_hw *hw, struct ieee80211_txq *txq,
bool withoutpkts)
{
TODO();
}
static __inline void
ieee80211_beacon_set_cntdwn(struct ieee80211_vif *vif, u8 counter)
{

View File

@ -76,6 +76,12 @@ __FBSDID("$FreeBSD$");
static MALLOC_DEFINE(M_LKPI80211, "lkpi80211", "LinuxKPI 80211 compat");
/* XXX-BZ really want this and others in queue.h */
#define TAILQ_ELEM_INIT(elm, field) do { \
(elm)->field.tqe_next = NULL; \
(elm)->field.tqe_prev = NULL; \
} while (0)
/* -------------------------------------------------------------------------- */
/* Keep public for as long as header files are using it too. */
@ -4620,6 +4626,220 @@ linuxkpi_ieee80211_beacon_loss(struct ieee80211_vif *vif)
/* -------------------------------------------------------------------------- */
void
linuxkpi_ieee80211_stop_queue(struct ieee80211_hw *hw, int qnum)
{
struct lkpi_hw *lhw;
struct lkpi_vif *lvif;
struct ieee80211_vif *vif;
int ac_count, ac;
KASSERT(qnum < hw->queues, ("%s: qnum %d >= hw->queues %d, hw %p\n",
__func__, qnum, hw->queues, hw));
lhw = wiphy_priv(hw->wiphy);
/* See lkpi_ic_vap_create(). */
if (hw->queues >= IEEE80211_NUM_ACS)
ac_count = IEEE80211_NUM_ACS;
else
ac_count = 1;
LKPI_80211_LHW_LVIF_LOCK(lhw);
TAILQ_FOREACH(lvif, &lhw->lvif_head, lvif_entry) {
vif = LVIF_TO_VIF(lvif);
for (ac = 0; ac < ac_count; ac++) {
IMPROVE_TXQ("LOCKING");
if (qnum == vif->hw_queue[ac]) {
/*
* For now log this to better understand
* how this is supposed to work.
*/
if (lvif->hw_queue_stopped[ac])
ic_printf(lhw->ic, "%s:%d: lhw %p hw %p "
"lvif %p vif %p ac %d qnum %d already "
"stopped\n", __func__, __LINE__,
lhw, hw, lvif, vif, ac, qnum);
lvif->hw_queue_stopped[ac] = true;
}
}
}
LKPI_80211_LHW_LVIF_UNLOCK(lhw);
}
void
linuxkpi_ieee80211_stop_queues(struct ieee80211_hw *hw)
{
int i;
IMPROVE_TXQ("Locking; do we need further info?");
for (i = 0; i < hw->queues; i++)
linuxkpi_ieee80211_stop_queue(hw, i);
}
static void
lkpi_ieee80211_wake_queues(struct ieee80211_hw *hw, int hwq)
{
struct lkpi_hw *lhw;
struct lkpi_vif *lvif;
struct lkpi_sta *lsta;
int ac_count, ac, tid;
/* See lkpi_ic_vap_create(). */
if (hw->queues >= IEEE80211_NUM_ACS)
ac_count = IEEE80211_NUM_ACS;
else
ac_count = 1;
lhw = wiphy_priv(hw->wiphy);
IMPROVE_TXQ("Locking");
LKPI_80211_LHW_LVIF_LOCK(lhw);
TAILQ_FOREACH(lvif, &lhw->lvif_head, lvif_entry) {
struct ieee80211_vif *vif;
vif = LVIF_TO_VIF(lvif);
for (ac = 0; ac < ac_count; ac++) {
if (hwq == vif->hw_queue[ac]) {
/* XXX-BZ what about software scan? */
/*
* For now log this to better understand
* how this is supposed to work.
*/
if (!lvif->hw_queue_stopped[ac])
ic_printf(lhw->ic, "%s:%d: lhw %p hw %p "
"lvif %p vif %p ac %d hw_q not stopped\n",
__func__, __LINE__,
lhw, hw, lvif, vif, ac);
lvif->hw_queue_stopped[ac] = false;
LKPI_80211_LVIF_LOCK(lvif);
TAILQ_FOREACH(lsta, &lvif->lsta_head, lsta_entry) {
struct ieee80211_sta *sta;
sta = LSTA_TO_STA(lsta);
for (tid = 0; tid < nitems(sta->txq); tid++) {
struct lkpi_txq *ltxq;
if (sta->txq[tid] == NULL)
continue;
if (sta->txq[tid]->ac != ac)
continue;
ltxq = TXQ_TO_LTXQ(sta->txq[tid]);
if (!ltxq->stopped)
continue;
ltxq->stopped = false;
/* XXX-BZ see when this explodes with all the locking. taskq? */
lkpi_80211_mo_wake_tx_queue(hw, sta->txq[tid]);
}
}
LKPI_80211_LVIF_UNLOCK(lvif);
}
}
}
LKPI_80211_LHW_LVIF_UNLOCK(lhw);
}
void
linuxkpi_ieee80211_wake_queues(struct ieee80211_hw *hw)
{
int i;
IMPROVE_TXQ("Is this all/enough here?");
for (i = 0; i < hw->queues; i++)
lkpi_ieee80211_wake_queues(hw, i);
}
void
linuxkpi_ieee80211_wake_queue(struct ieee80211_hw *hw, int qnum)
{
KASSERT(qnum < hw->queues, ("%s: qnum %d >= hw->queues %d, hw %p\n",
__func__, qnum, hw->queues, hw));
lkpi_ieee80211_wake_queues(hw, qnum);
}
/* This is just hardware queues. */
void
linuxkpi_ieee80211_txq_schedule_start(struct ieee80211_hw *hw, uint8_t ac)
{
struct lkpi_hw *lhw;
lhw = HW_TO_LHW(hw);
IMPROVE_TXQ("Are there reasons why we wouldn't schedule?");
IMPROVE_TXQ("LOCKING");
if (++lhw->txq_generation[ac] == 0)
lhw->txq_generation[ac]++;
}
struct ieee80211_txq *
linuxkpi_ieee80211_next_txq(struct ieee80211_hw *hw, uint8_t ac)
{
struct lkpi_hw *lhw;
struct ieee80211_txq *txq;
struct lkpi_txq *ltxq;
lhw = HW_TO_LHW(hw);
txq = NULL;
IMPROVE_TXQ("LOCKING");
/* Check that we are scheduled. */
if (lhw->txq_generation[ac] == 0)
goto out;
ltxq = TAILQ_FIRST(&lhw->scheduled_txqs[ac]);
if (ltxq == NULL)
goto out;
if (ltxq->txq_generation == lhw->txq_generation[ac])
goto out;
ltxq->txq_generation = lhw->txq_generation[ac];
TAILQ_REMOVE(&lhw->scheduled_txqs[ac], ltxq, txq_entry);
txq = &ltxq->txq;
TAILQ_ELEM_INIT(ltxq, txq_entry);
out:
return (txq);
}
void linuxkpi_ieee80211_schedule_txq(struct ieee80211_hw *hw,
struct ieee80211_txq *txq, bool withoutpkts)
{
struct lkpi_hw *lhw;
struct lkpi_txq *ltxq;
ltxq = TXQ_TO_LTXQ(txq);
IMPROVE_TXQ("LOCKING");
/* Only schedule if work to do or asked to anyway. */
if (!withoutpkts && skb_queue_empty(&ltxq->skbq))
goto out;
/* Make sure we do not double-schedule. */
if (ltxq->txq_entry.tqe_next != NULL)
goto out;
lhw = HW_TO_LHW(hw);
TAILQ_INSERT_TAIL(&lhw->scheduled_txqs[txq->ac], ltxq, txq_entry);
out:
return;
}
/* -------------------------------------------------------------------------- */
struct lkpi_cfg80211_bss {
u_int refcnt;
struct cfg80211_bss bss;

View File

@ -50,6 +50,7 @@
#ifndef D80211_IMPROVE
#define D80211_IMPROVE 0x2
#endif
#define D80211_IMPROVE_TXQ 0x4
#define D80211_TRACE 0x10
#define D80211_TRACEOK 0x20
#define D80211_TRACE_TX 0x100
@ -62,6 +63,10 @@
#define D80211_TRACE_STA 0x10000
#define D80211_TRACE_MO 0x100000
#define IMPROVE_TXQ(...) \
if (linuxkpi_debug_80211 & D80211_IMPROVE_TXQ) \
printf("%s:%d: XXX LKPI80211 IMPROVE_TXQ\n", __func__, __LINE__)
struct lkpi_radiotap_tx_hdr {
struct ieee80211_radiotap_header wt_ihdr;
uint8_t wt_flags;
@ -93,7 +98,11 @@ struct lkpi_radiotap_rx_hdr {
(1 << IEEE80211_RADIOTAP_DBM_ANTNOISE))
struct lkpi_txq {
TAILQ_ENTRY(lkpi_txq) txq_entry;
bool seen_dequeue;
bool stopped;
uint32_t txq_generation;
struct sk_buff_head skbq;
/* Must be last! */
@ -139,6 +148,8 @@ struct lkpi_vif {
TAILQ_HEAD(, lkpi_sta) lsta_head;
bool added_to_drv; /* Driver knows; i.e. we called add_interface(). */
bool hw_queue_stopped[IEEE80211_NUM_ACS];
/* Must be last! */
struct ieee80211_vif vif __aligned(CACHE_LINE_SIZE);
};
@ -164,6 +175,9 @@ struct lkpi_hw { /* name it mac80211_sc? */
struct mtx mtx;
uint32_t txq_generation[IEEE80211_NUM_ACS];
TAILQ_HEAD(, lkpi_txq) scheduled_txqs[IEEE80211_NUM_ACS];
/* Scan functions we overload to handle depending on scan mode. */
void (*ic_scan_curchan)(struct ieee80211_scan_state *,
unsigned long);