git: 5a9a0d780338 - main - LinuxKPI: 802.11: basic implementation of *queue(s)/*txq*

From: Bjoern A. Zeeb <bz_at_FreeBSD.org>
Date: Tue, 31 Jan 2023 16:19:33 UTC
The branch main has been updated by bz:

URL: https://cgit.FreeBSD.org/src/commit/?id=5a9a0d7803382321b5f9fff1deae5fb08463cf1a

commit 5a9a0d7803382321b5f9fff1deae5fb08463cf1a
Author:     Bjoern A. Zeeb <bz@FreeBSD.org>
AuthorDate: 2023-01-31 16:17:14 +0000
Commit:     Bjoern A. Zeeb <bz@FreeBSD.org>
CommitDate: 2023-01-31 16:17:14 +0000

    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
---
 sys/compat/linuxkpi/common/include/net/mac80211.h | 124 ++++++------
 sys/compat/linuxkpi/common/src/linux_80211.c      | 220 ++++++++++++++++++++++
 sys/compat/linuxkpi/common/src/linux_80211.h      |  14 ++
 3 files changed, 299 insertions(+), 59 deletions(-)

diff --git a/sys/compat/linuxkpi/common/include/net/mac80211.h b/sys/compat/linuxkpi/common/include/net/mac80211.h
index 2b3f6b8e3ab1..93382a4bd424 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);
 
 /* -------------------------------------------------------------------------- */
 
@@ -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)
 {
diff --git a/sys/compat/linuxkpi/common/src/linux_80211.c b/sys/compat/linuxkpi/common/src/linux_80211.c
index 3aff01baef2c..cf0c1755c499 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. */
@@ -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;
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);