Download raw body.
sys/qwz: add AMPDU callbacks
tech@,
here I added the same BlockAck task and AMPDU callback plumbing used by qwx.
This wires net80211 ADDBA/DELBA handling into the existing qwz RX
TID/reorder setup code, while leaving TX aggregation to firmware as qwx.
My iperf benchmark with https://marc.info/?l=openbsd-tech&m=177978909323116&w=2
just iperf:
[ ID] Interval Transfer Bitrate Retr
[ 4] 0.00-30.01 sec 713 MBytes 199 Mbits/sec 0 sender
[ 4] 0.00-30.22 sec 713 MBytes 198 Mbits/sec receiver
and iperf -R:
[ ID] Interval Transfer Bitrate Retr
[ 4] 0.00-30.23 sec 639 MBytes 177 Mbits/sec 31856 sender
[ 4] 0.00-30.02 sec 638 MBytes 178 Mbits/sec receiver
Ok?
Index: sys/dev/ic/qwz.c
===================================================================
RCS file: /home/cvs/src/sys/dev/ic/qwz.c,v
diff -u -p -r1.36 qwz.c
--- sys/dev/ic/qwz.c 25 May 2026 20:33:58 -0000 1.36
+++ sys/dev/ic/qwz.c 26 May 2026 13:11:21 -0000
@@ -345,6 +345,7 @@ qwz_stop(struct ifnet *ifp)
task_del(systq, &sc->init_task);
qwz_del_task(sc, sc->sc_nswq, &sc->newstate_task);
qwz_del_task(sc, systq, &sc->setkey_task);
+ qwz_del_task(sc, systq, &sc->ba_task);
refcnt_finalize(&sc->task_refs, "qwzstop");
qwz_setkey_clear(sc);
@@ -868,9 +869,7 @@ qwz_newstate(struct ieee80211com *ic, en
nstate != IEEE80211_S_AUTH)
return 0;
if (ic->ic_state == IEEE80211_S_RUN) {
-#if 0
qwz_del_task(sc, systq, &sc->ba_task);
-#endif
qwz_del_task(sc, systq, &sc->setkey_task);
qwz_setkey_clear(sc);
#if 0
@@ -24432,6 +24431,116 @@ qwz_peer_assoc_prepare(struct qwz_softc
/* TODO: amsdu_disable req? */
}
+void
+qwz_rx_agg_start(struct qwz_softc *sc, struct ieee80211_node *ni, uint8_t tid,
+ uint16_t ssn, uint16_t winsize)
+{
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct qwz_vif *arvif = TAILQ_FIRST(&sc->vif_list); /* XXX */
+ uint8_t pdev_id = 0; /* XXX derive pdev ID somehow */
+ enum hal_pn_type pn_type;
+
+ if (!test_bit(ATH12K_FLAG_HW_CRYPTO_DISABLED, sc->sc_flags) &&
+ (ic->ic_flags & IEEE80211_F_RSNON))
+ pn_type = HAL_PN_TYPE_WPA;
+ else
+ pn_type = HAL_PN_TYPE_NONE;
+
+ if (qwz_peer_rx_tid_setup(sc, ni, arvif->vdev_id, pdev_id, tid,
+ winsize, ssn, pn_type))
+ ieee80211_addba_req_refuse(ic, ni, tid);
+ else
+ ieee80211_addba_req_accept(ic, ni, tid);
+}
+
+void
+qwz_rx_agg_stop(struct qwz_softc *sc, struct ieee80211_node *ni, uint8_t tid)
+{
+ struct qwz_vif *arvif = TAILQ_FIRST(&sc->vif_list); /* XXX */
+ uint8_t pdev_id = 0; /* XXX derive pdev ID somehow */
+ struct qwz_node *nq = (struct qwz_node *)ni;
+ struct ath12k_peer *peer = &nq->peer;
+ uint64_t paddr;
+ int ret;
+
+ if (peer->peer_id == HAL_INVALID_PEERID)
+ return;
+
+ if (!peer->rx_tid[tid].active)
+ return;
+
+ ret = qwz_peer_rx_tid_reo_update(sc, peer, &peer->rx_tid[tid],
+ 1, 0, 0);
+ if (ret) {
+ printf("%s: failed to update reo for rx tid %d: %d\n",
+ sc->sc_dev.dv_xname, tid, ret);
+ }
+
+ paddr = peer->rx_tid[tid].paddr;
+ ret = qwz_wmi_peer_rx_reorder_queue_setup(sc, arvif->vdev_id, pdev_id,
+ ni->ni_macaddr, paddr, tid, 1, 1);
+ if (ret) {
+ printf("%s: failed to send wmi to delete rx tid %d\n",
+ sc->sc_dev.dv_xname, ret);
+ }
+}
+
+void
+qwz_ba_task(void *arg)
+{
+ struct qwz_softc *sc = arg;
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211_node *ni = ic->ic_bss;
+ int s = splnet();
+ int tid;
+
+ for (tid = 0; tid < IEEE80211_NUM_TID; tid++) {
+ if (test_bit(ATH12K_FLAG_CRASH_FLUSH, sc->sc_flags))
+ break;
+ if (sc->ba_rx.start_tidmask & (1 << tid)) {
+ struct ieee80211_rx_ba *ba = &ni->ni_rx_ba[tid];
+ qwz_rx_agg_start(sc, ni, tid, ba->ba_winstart,
+ ba->ba_winsize);
+ sc->ba_rx.start_tidmask &= ~(1 << tid);
+ } else if (sc->ba_rx.stop_tidmask & (1 << tid)) {
+ qwz_rx_agg_stop(sc, ni, tid);
+ sc->ba_rx.stop_tidmask &= ~(1 << tid);
+ }
+ }
+
+ refcnt_rele_wake(&sc->task_refs);
+ splx(s);
+}
+
+int
+qwz_ampdu_rx_start(struct ieee80211com *ic, struct ieee80211_node *ni,
+ uint8_t tid)
+{
+ struct qwz_softc *sc = ic->ic_softc;
+
+ sc->ba_rx.start_tidmask |= (1 << tid);
+ qwz_add_task(sc, systq, &sc->ba_task);
+ return EBUSY;
+}
+
+void
+qwz_ampdu_rx_stop(struct ieee80211com *ic, struct ieee80211_node *ni,
+ uint8_t tid)
+{
+ struct qwz_softc *sc = ic->ic_softc;
+
+ sc->ba_rx.stop_tidmask |= (1 << tid);
+ qwz_add_task(sc, systq, &sc->ba_task);
+}
+
+int
+qwz_ampdu_tx_start(struct ieee80211com *ic, struct ieee80211_node *ni,
+ uint8_t tid)
+{
+ /* Firmware handles Tx aggregation internally. */
+ return 0;
+}
+
int
qwz_run(struct qwz_softc *sc)
{
@@ -24608,6 +24717,7 @@ qwz_attach(struct qwz_softc *sc)
task_set(&sc->init_task, qwz_init_task, sc);
task_set(&sc->newstate_task, qwz_newstate_task, sc);
task_set(&sc->setkey_task, qwz_setkey_task, sc);
+ task_set(&sc->ba_task, qwz_ba_task, sc);
timeout_set_proc(&sc->scan.timeout, qwz_scan_timeout, sc);
#if NBPFILTER > 0
qwz_radiotap_attach(sc);
Index: sys/dev/ic/qwzvar.h
===================================================================
RCS file: /home/cvs/src/sys/dev/ic/qwzvar.h,v
diff -u -p -r1.17 qwzvar.h
--- sys/dev/ic/qwzvar.h 19 May 2026 04:17:51 -0000 1.17
+++ sys/dev/ic/qwzvar.h 26 May 2026 13:10:36 -0000
@@ -1910,6 +1910,11 @@ struct qwz_setkey_task_arg {
#define QWZ_DEL_KEY 2
};
+struct qwz_ba_task_data {
+ uint32_t start_tidmask;
+ uint32_t stop_tidmask;
+};
+
struct qwz_softc {
struct device sc_dev;
struct ieee80211com sc_ic;
@@ -1944,6 +1949,10 @@ struct qwz_softc {
int install_key_done;
int install_key_status;
+ /* Task for firmware BlockAck setup/teardown and its arguments. */
+ struct task ba_task;
+ struct qwz_ba_task_data ba_rx;
+
enum ath12k_11d_state state_11d;
int completed_11d_scan;
uint32_t vdev_id_11d_scan;
@@ -2161,6 +2170,12 @@ int qwz_set_key(struct ieee80211com *, s
struct ieee80211_key *);
void qwz_delete_key(struct ieee80211com *, struct ieee80211_node *,
struct ieee80211_key *);
+int qwz_ampdu_rx_start(struct ieee80211com *, struct ieee80211_node *,
+ uint8_t);
+void qwz_ampdu_rx_stop(struct ieee80211com *, struct ieee80211_node *,
+ uint8_t);
+int qwz_ampdu_tx_start(struct ieee80211com *, struct ieee80211_node *,
+ uint8_t);
void qwz_qrtr_recv_msg(struct qwz_softc *, struct mbuf *);
Index: sys/dev/pci/if_qwz_pci.c
===================================================================
RCS file: /home/cvs/src/sys/dev/pci/if_qwz_pci.c,v
diff -u -p -r1.11 if_qwz_pci.c
--- sys/dev/pci/if_qwz_pci.c 18 May 2026 13:47:32 -0000 1.11
+++ sys/dev/pci/if_qwz_pci.c 26 May 2026 13:10:36 -0000
@@ -1007,6 +1007,10 @@ qwz_pci_attach(struct device *parent, st
ic->ic_updateedca = qwz_updateedca;
ic->ic_updatedtim = qwz_updatedtim;
#endif
+ ic->ic_ampdu_rx_start = qwz_ampdu_rx_start;
+ ic->ic_ampdu_rx_stop = qwz_ampdu_rx_stop;
+ ic->ic_ampdu_tx_start = qwz_ampdu_tx_start;
+ ic->ic_ampdu_tx_stop = NULL;
/*
* We cannot read the MAC address without loading the
* firmware from disk. Postpone until mountroot is done.
--
wbr, Kirill
sys/qwz: add AMPDU callbacks