aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBjoern A. Zeeb <bz@FreeBSD.org>2023-01-31 16:17:14 +0000
committerBjoern A. Zeeb <bz@FreeBSD.org>2023-02-23 19:33:01 +0000
commit0b165b1fce848c6c636548d54064eec5a9d82c55 (patch)
tree61bf8696feef4a1af65d9e70691d75cf2dcefb29
parent8e45120aceeb5a1457b9406c9b1543e5a1d1d314 (diff)
downloadsrc-0b165b1fce848c6c636548d54064eec5a9d82c55.tar.gz
src-0b165b1fce848c6c636548d54064eec5a9d82c55.zip
LinuxKPI: 802.11: basic implementation of *queue(s)/*txq*
LinuxKPI: 802.11: deal with stopped queues 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. Following 5a9a0d7803382321b5f9fff1deae5fb08463cf1a initialize the queue values explicitly and deal with a stopped queue in ieee80211_tx_dequeue(). Sponsored by: The FreeBSD Foundation (parts of this work) Approved by: re (cperciva) (cherry picked from commit 5a9a0d7803382321b5f9fff1deae5fb08463cf1a) (cherry picked from commit 0cbcfa1964de89cd346ee6f79437c6ab83a3b716) (cherry picked from commit 9f9d047405778b2d2aca829a2037532b8ae8ed5d)
-rw-r--r--sys/compat/linuxkpi/common/include/net/mac80211.h124
-rw-r--r--sys/compat/linuxkpi/common/src/linux_80211.c237
-rw-r--r--sys/compat/linuxkpi/common/src/linux_80211.h14
3 files changed, 316 insertions, 59 deletions
diff --git a/sys/compat/linuxkpi/common/include/net/mac80211.h b/sys/compat/linuxkpi/common/include/net/mac80211.h
index b7c6c2d37b90..36e6600f237b 100644
--- a/sys/compat/linuxkpi/common/include/net/mac80211.h
+++ b/sys/compat/linuxkpi/common/include/net/mac80211.h
@@ -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);
/* -------------------------------------------------------------------------- */
@@ -1508,6 +1516,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)
{
@@ -1820,18 +1885,6 @@ ieee80211_tdls_oper_request(struct ieee80211_vif *vif, uint8_t *addr,
}
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)
{
TODO();
@@ -2118,18 +2171,6 @@ ieee80211_queue_work(struct ieee80211_hw *hw, struct work_struct *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)
{
@@ -2259,41 +2300,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)
{
diff --git a/sys/compat/linuxkpi/common/src/linux_80211.c b/sys/compat/linuxkpi/common/src/linux_80211.c
index bcd8ea9d4c6e..d0a3b4b8586a 100644
--- a/sys/compat/linuxkpi/common/src/linux_80211.c
+++ b/sys/compat/linuxkpi/common/src/linux_80211.c
@@ -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. */
@@ -238,9 +244,11 @@ lkpi_lsta_alloc(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN],
ltxq->txq.ac = tid_to_mac80211_ac[tid & 7];
}
ltxq->seen_dequeue = false;
+ ltxq->stopped = false;
ltxq->txq.vif = vif;
ltxq->txq.tid = tid;
ltxq->txq.sta = sta;
+ TAILQ_ELEM_INIT(ltxq, txq_entry);
skb_queue_head_init(&ltxq->skbq);
sta->txq[tid] = &ltxq->txq;
}
@@ -2270,6 +2278,9 @@ lkpi_ic_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ],
vif->hw_queue[i] = i;
else
vif->hw_queue[i] = 0;
+
+ /* Initialize the queue to running. Stopped? */
+ lvif->hw_queue_stopped[i] = false;
}
vif->cab_queue = IEEE80211_INVAL_HW_QUEUE;
@@ -4262,13 +4273,25 @@ linuxkpi_ieee80211_tx_dequeue(struct ieee80211_hw *hw,
struct ieee80211_txq *txq)
{
struct lkpi_txq *ltxq;
+ struct lkpi_vif *lvif;
struct sk_buff *skb;
+ skb = NULL;
ltxq = TXQ_TO_LTXQ(txq);
ltxq->seen_dequeue = true;
+ if (ltxq->stopped)
+ goto stopped;
+
+ lvif = VIF_TO_LVIF(ltxq->txq.vif);
+ if (lvif->hw_queue_stopped[ltxq->txq.ac]) {
+ ltxq->stopped = true;
+ goto stopped;
+ }
+
skb = skb_dequeue(&ltxq->skbq);
+stopped:
return (skb);
}
@@ -4643,6 +4666,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;
diff --git a/sys/compat/linuxkpi/common/src/linux_80211.h b/sys/compat/linuxkpi/common/src/linux_80211.h
index d9f2ce68f4f1..4d44ca07948e 100644
--- a/sys/compat/linuxkpi/common/src/linux_80211.h
+++ b/sys/compat/linuxkpi/common/src/linux_80211.h
@@ -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);