From 3e8166c7d5c29c3daa8fadd9257d6bbcd73fd3e0 Mon Sep 17 00:00:00 2001 From: John Baldwin Date: Mon, 24 Mar 2025 11:50:37 -0400 Subject: [PATCH 01/14] csu: Define a "csu" compartment policy This policy file is installed to /usr/lib alongside the relevant crt* object files. bsd.(lib|prog).mk add this policy to LDFLAGS when linking an output that already has at least one other policy file. --- lib/csu/Makefile.inc | 2 +- lib/csu/common/crt.json | 16 ++++++++++++++++ share/mk/bsd.lib.mk | 1 + share/mk/bsd.prog.mk | 1 + 4 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 lib/csu/common/crt.json diff --git a/lib/csu/Makefile.inc b/lib/csu/Makefile.inc index 7749bfed6860..f70788b0832b 100644 --- a/lib/csu/Makefile.inc +++ b/lib/csu/Makefile.inc @@ -36,7 +36,7 @@ CFLAGS+= -I${.CURDIR:H}/common-cheri CFLAGS_CRTS+= -DSHARED ${PICFLAG} -FILES= ${OBJS} +FILES= ${OBJS} crt.json FILESMODE= ${LIBMODE} FILESOWN= ${LIBOWN} FILESGRP= ${LIBGRP} diff --git a/lib/csu/common/crt.json b/lib/csu/common/crt.json new file mode 100644 index 000000000000..9371dbdb61be --- /dev/null +++ b/lib/csu/common/crt.json @@ -0,0 +1,16 @@ +{ + "compartments": { + "csu": { + "files": [ + "crt1.o", + "crtbegin.o", + "crtbeginS.o", + "crtbeginT.o", + "crtend.o", + "crtendS.o", + "crti.o", + "crtn.o" + ] + } + } +} diff --git a/share/mk/bsd.lib.mk b/share/mk/bsd.lib.mk index e9087a3219ed..8b16d3d1a551 100644 --- a/share/mk/bsd.lib.mk +++ b/share/mk/bsd.lib.mk @@ -299,6 +299,7 @@ LDFLAGS+= -Wl,--undefined-version .if ${MK_COMPARTMENT_POLICY} != "no" && ${MACHINE_ABI:Mpurecap} .if !empty(COMPARTMENT_POLICY) +COMPARTMENT_POLICY+= ${SYSROOT}/usr/lib/crt.json ${SHLIB_NAME_FULL}: ${COMPARTMENT_POLICY} LDFLAGS+= ${COMPARTMENT_POLICY:S/^/-Wl,--compartment-policy=/} .endif diff --git a/share/mk/bsd.prog.mk b/share/mk/bsd.prog.mk index 2f9c21c23258..13795187ac1d 100644 --- a/share/mk/bsd.prog.mk +++ b/share/mk/bsd.prog.mk @@ -177,6 +177,7 @@ PROG_FULL= ${PROG} .if ${MK_COMPARTMENT_POLICY} != "no" && ${MACHINE_ABI:Mpurecap} .if !empty(COMPARTMENT_POLICY) +COMPARTMENT_POLICY+= ${SYSROOT}/usr/lib/crt.json ${PROG_FULL}: ${COMPARTMENT_POLICY} LDFLAGS+= ${COMPARTMENT_POLICY:S/^/-Wl,--compartment-policy=/} .endif From 3f77825c02ea40ebb6171568721c94bc8b1cd51f Mon Sep 17 00:00:00 2001 From: Brooks Davis Date: Thu, 27 Mar 2025 22:42:55 +0000 Subject: [PATCH 02/14] compiler-rt: improve _Float16 and __bf16 guards Tighten the bounds on enabling _Float16 and __bf16 to not include development 15.0.0 and development versions there of (including the release/15.x branch point). __bf16 support was enabled very late in the 15.0.0 release cycle, just prior to rc3. This caused problems building amd64 targets with a Morello LLVM compiler synced to near the 15.x branch point. --- lib/libcompiler_rt/Makefile.inc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/libcompiler_rt/Makefile.inc b/lib/libcompiler_rt/Makefile.inc index 3628024c45d6..bd9e9299f805 100644 --- a/lib/libcompiler_rt/Makefile.inc +++ b/lib/libcompiler_rt/Makefile.inc @@ -252,7 +252,7 @@ CRT_COMMON_F16_ARCH=t # _Float16 support, only on some architectures, and with certain compiler # versions. # -.if ((${COMPILER_TYPE} == "clang" && ${COMPILER_VERSION} >= 150000) && \ +.if ((${COMPILER_TYPE} == "clang" && ${COMPILER_VERSION} >= 150001) && \ (defined(CRT_COMMON_F16_ARCH) || \ ${MACHINE_CPUARCH} == "arm" || ${MACHINE_CPUARCH} == "riscv")) || \ ((${COMPILER_TYPE} == "gcc" && ${COMPILER_VERSION} >= 120000) && \ @@ -264,7 +264,7 @@ CFLAGS+= -DCOMPILER_RT_HAS_FLOAT16 # __bf16 support, only on some architectures, and with certain compiler # versions. # -.if ((${COMPILER_TYPE} == "clang" && ${COMPILER_VERSION} >= 150000) && \ +.if ((${COMPILER_TYPE} == "clang" && ${COMPILER_VERSION} >= 150001) && \ (defined(CRT_COMMON_F16_ARCH)) && ${MACHINE_CPUARCH} != "aarch64") || \ ((${COMPILER_TYPE} == "clang" && ${COMPILER_VERSION} >= 160000) && \ ${MACHINE_CPUARCH} == "aarch64") || \ From 4bfb129ae692ff3ab912867ee934cc0d6453a234 Mon Sep 17 00:00:00 2001 From: Adrian Chadd Date: Fri, 8 Nov 2024 08:26:05 -0800 Subject: [PATCH 03/14] rtwn: ensure TX work isn't scheduled during reset / abort Don't schedule work during reset / abort. For USB NICs, work must not be scheduled during a call to rtwn_usb_abort_xfers(), as then it'll cause the call to usbd_transfer_drain() to hang. This fixes a hang I've been seeing where the NIC hits a TX timeout and then the reset/re-init path is called. If data is scheduled to be transmitted in that window, the call to usbd_transfer_drain() would hang and require a hard reboot to recover. Differential Revision: https://reviews.freebsd.org/D47479 (cherry picked from commit 8838f3c32ac0ebcb8b20863f8c455375039a505e) --- sys/dev/rtwn/if_rtwn_tx.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sys/dev/rtwn/if_rtwn_tx.c b/sys/dev/rtwn/if_rtwn_tx.c index f5e97933b314..bf45d14f7edc 100644 --- a/sys/dev/rtwn/if_rtwn_tx.c +++ b/sys/dev/rtwn/if_rtwn_tx.c @@ -263,6 +263,11 @@ rtwn_start(struct rtwn_softc *sc) struct mbuf *m; RTWN_ASSERT_LOCKED(sc); + + /* Ensure no work is scheduled during reset/teardown */ + if ((sc->sc_flags & RTWN_RUNNING) == 0) + return; + while ((m = mbufq_dequeue(&sc->sc_snd)) != NULL) { if (sc->qfullmsk != 0) { mbufq_prepend(&sc->sc_snd, m); From d2ef0825775c113b05f686de68f6b2217f30898e Mon Sep 17 00:00:00 2001 From: Adrian Chadd Date: Mon, 11 Nov 2024 20:48:12 -0800 Subject: [PATCH 04/14] rtwn: change the USB TX transfers to only do one pending transfer per endpoint I found I was getting constant device timeouts when doing anything more complicated than a single SSH on laptop with RTL8811AU. After digging into it, i found a variety of fun situations, including traffic stalls that would recover w/ a shorter (1 second) USB transfer timeout. However, the big one is a straight up hang of any TX endpoint until the NIC was reset. The RX side kept going just fine; only the TX endpoints would hang. Reproducing it was easy - just start up a couple of traffic streams on different WME AC's - eg a best effort + bulk transfer, like browsing the web and doing an ssh clone - throw in a ping -i 0.1 to your gateway, and it would very quickly hit device timeouts every couple of seconds. I put everything into a single TX EP and the hangs went away. Well, mostly. So after some MORE digging, I found that this driver isn't checking if the transfers are going into the correct EPs for the packet WME access category / 802.11 TID; and would frequently be able to schedule multiple transfers into the same endpoint. Then there's a second problem - there's an array of endpoints used for setting up the USB device, with .endpoint = UE_ADDR_ANY, however they're also being setup with the same endpoint configured in multiple transfer configs. Eg, a NIC with 3 or 4 bulk TX endpoints will configure the BK and BE endpoints with the same physical endpoint ID. This also leads to timed out transfers. My /guess/ was that the firmware isn't happy with one or both of the above, and so I solved both. * drop the USB transfer timeout to 1 second, not 5 seconds - that way we'll either get a 1 second traffic pause and USB transfer failure, or a 5 second device timeout. Having both the TX timeout and the USB transfer timeout made recovery from a USB transfer timeout (without a NIC reset) almost impossible. * enforce one transfer per endpoint; * separate pending/active buffer tracking per endpoint; * each endpoint now has its own TX callback to make sure the queue / end point ID is known; * and only frames from a given endpoint pending queue is going into the active queue and into that endpoint. * Finally, create a local wme2qid array and populate it with the endpoint mapping that ensures unique physical endpoint use. Locally tested: * rtl8812AU, 11n STA mode * rtl8192EU, 11n STA mode (with diffs to fix the channel config / power timeouts.) Differential Revision: https://reviews.freebsd.org/D47522 (cherry picked from commit d99eb8230eb717ab0b2eba948614d0f2f2b5dd2b) --- sys/dev/rtwn/if_rtwnvar.h | 2 +- sys/dev/rtwn/usb/rtwn_usb_attach.c | 26 +++++--- sys/dev/rtwn/usb/rtwn_usb_ep.c | 23 +++++-- sys/dev/rtwn/usb/rtwn_usb_tx.c | 100 +++++++++++++++++++++++------ sys/dev/rtwn/usb/rtwn_usb_tx.h | 5 +- sys/dev/rtwn/usb/rtwn_usb_var.h | 12 ++-- 6 files changed, 129 insertions(+), 39 deletions(-) diff --git a/sys/dev/rtwn/if_rtwnvar.h b/sys/dev/rtwn/if_rtwnvar.h index 6a44b7b73902..f4c6d7ee64b4 100644 --- a/sys/dev/rtwn/if_rtwnvar.h +++ b/sys/dev/rtwn/if_rtwnvar.h @@ -32,7 +32,7 @@ #define RTWN_MACID_VALID 0x8000 #define RTWN_MACID_LIMIT 128 -#define RTWN_TX_TIMEOUT 5000 /* ms */ +#define RTWN_TX_TIMEOUT 1000 /* ms */ #define RTWN_MAX_EPOUT 4 #define RTWN_PORT_COUNT 2 diff --git a/sys/dev/rtwn/usb/rtwn_usb_attach.c b/sys/dev/rtwn/usb/rtwn_usb_attach.c index 71798ffc14f9..4958939a768a 100644 --- a/sys/dev/rtwn/usb/rtwn_usb_attach.c +++ b/sys/dev/rtwn/usb/rtwn_usb_attach.c @@ -156,10 +156,12 @@ rtwn_usb_alloc_tx_list(struct rtwn_softc *sc) if (error != 0) return (error); - STAILQ_INIT(&uc->uc_tx_active); - STAILQ_INIT(&uc->uc_tx_inactive); - STAILQ_INIT(&uc->uc_tx_pending); + for (i = RTWN_BULK_TX_FIRST; i < RTWN_BULK_EP_COUNT; i++) { + STAILQ_INIT(&uc->uc_tx_active[i]); + STAILQ_INIT(&uc->uc_tx_pending[i]); + } + STAILQ_INIT(&uc->uc_tx_inactive); for (i = 0; i < RTWN_USB_TX_LIST_COUNT; i++) STAILQ_INSERT_HEAD(&uc->uc_tx_inactive, &uc->uc_tx[i], next); @@ -207,23 +209,29 @@ static void rtwn_usb_free_tx_list(struct rtwn_softc *sc) { struct rtwn_usb_softc *uc = RTWN_USB_SOFTC(sc); + int i; rtwn_usb_free_list(sc, uc->uc_tx, RTWN_USB_TX_LIST_COUNT); - STAILQ_INIT(&uc->uc_tx_active); + for (i = RTWN_BULK_TX_FIRST; i < RTWN_BULK_EP_COUNT; i++) { + STAILQ_INIT(&uc->uc_tx_active[i]); + STAILQ_INIT(&uc->uc_tx_pending[i]); + } STAILQ_INIT(&uc->uc_tx_inactive); - STAILQ_INIT(&uc->uc_tx_pending); } static void rtwn_usb_reset_lists(struct rtwn_softc *sc, struct ieee80211vap *vap) { struct rtwn_usb_softc *uc = RTWN_USB_SOFTC(sc); + int i; RTWN_ASSERT_LOCKED(sc); - rtwn_usb_reset_tx_list(uc, &uc->uc_tx_active, vap); - rtwn_usb_reset_tx_list(uc, &uc->uc_tx_pending, vap); + for (i = RTWN_BULK_TX_FIRST; i < RTWN_BULK_EP_COUNT; i++) { + rtwn_usb_reset_tx_list(uc, &uc->uc_tx_active[i], vap); + rtwn_usb_reset_tx_list(uc, &uc->uc_tx_pending[i], vap); + } if (vap == NULL) { rtwn_usb_reset_rx_list(uc); sc->qfullmsk = 0; @@ -295,7 +303,7 @@ rtwn_usb_abort_xfers(struct rtwn_softc *sc) /* abort any pending transfers */ RTWN_UNLOCK(sc); - for (i = 0; i < RTWN_N_TRANSFER; i++) + for (i = 0; i < RTWN_BULK_EP_COUNT; i++) usbd_transfer_drain(uc->uc_xfer[i]); RTWN_LOCK(sc); } @@ -432,7 +440,7 @@ rtwn_usb_detach(device_t self) rtwn_usb_free_rx_list(sc); /* Detach all USB transfers. */ - usbd_transfer_unsetup(uc->uc_xfer, RTWN_N_TRANSFER); + usbd_transfer_unsetup(uc->uc_xfer, RTWN_BULK_EP_COUNT); rtwn_detach_private(sc); mtx_destroy(&sc->sc_mtx); diff --git a/sys/dev/rtwn/usb/rtwn_usb_ep.c b/sys/dev/rtwn/usb/rtwn_usb_ep.c index 0848a45a9f86..f9b0672324fe 100644 --- a/sys/dev/rtwn/usb/rtwn_usb_ep.c +++ b/sys/dev/rtwn/usb/rtwn_usb_ep.c @@ -55,7 +55,7 @@ #include -static const struct usb_config rtwn_config_common[RTWN_N_TRANSFER] = { +static const struct usb_config rtwn_config_common[RTWN_BULK_EP_COUNT] = { [RTWN_BULK_RX] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, @@ -76,7 +76,7 @@ static const struct usb_config rtwn_config_common[RTWN_N_TRANSFER] = { .pipe_bof = 1, .force_short_xfer = 1, }, - .callback = rtwn_bulk_tx_callback, + .callback = rtwn_bulk_tx_callback_be, .timeout = RTWN_TX_TIMEOUT, /* ms */ }, [RTWN_BULK_TX_BK] = { @@ -89,7 +89,7 @@ static const struct usb_config rtwn_config_common[RTWN_N_TRANSFER] = { .pipe_bof = 1, .force_short_xfer = 1, }, - .callback = rtwn_bulk_tx_callback, + .callback = rtwn_bulk_tx_callback_bk, .timeout = RTWN_TX_TIMEOUT, /* ms */ }, [RTWN_BULK_TX_VI] = { @@ -102,7 +102,7 @@ static const struct usb_config rtwn_config_common[RTWN_N_TRANSFER] = { .pipe_bof = 1, .force_short_xfer = 1 }, - .callback = rtwn_bulk_tx_callback, + .callback = rtwn_bulk_tx_callback_vi, .timeout = RTWN_TX_TIMEOUT, /* ms */ }, [RTWN_BULK_TX_VO] = { @@ -115,7 +115,7 @@ static const struct usb_config rtwn_config_common[RTWN_N_TRANSFER] = { .pipe_bof = 1, .force_short_xfer = 1 }, - .callback = rtwn_bulk_tx_callback, + .callback = rtwn_bulk_tx_callback_vo, .timeout = RTWN_TX_TIMEOUT, /* ms */ }, }; @@ -200,22 +200,33 @@ rtwn_usb_setup_endpoints(struct rtwn_usb_softc *uc) /* NB: keep in sync with rtwn_dma_init(). */ rtwn_config[RTWN_BULK_TX_VO].endpoint = addr[0]; + uc->wme2qid[WME_AC_VO] = RTWN_BULK_TX_VO; switch (uc->ntx) { case 4: case 3: rtwn_config[RTWN_BULK_TX_BE].endpoint = addr[2]; rtwn_config[RTWN_BULK_TX_BK].endpoint = addr[2]; rtwn_config[RTWN_BULK_TX_VI].endpoint = addr[1]; + uc->wme2qid[WME_AC_BE] = RTWN_BULK_TX_BE; + uc->wme2qid[WME_AC_BK] = RTWN_BULK_TX_BE; + uc->wme2qid[WME_AC_VI] = RTWN_BULK_TX_VI; break; case 2: rtwn_config[RTWN_BULK_TX_BE].endpoint = addr[1]; rtwn_config[RTWN_BULK_TX_BK].endpoint = addr[1]; rtwn_config[RTWN_BULK_TX_VI].endpoint = addr[0]; + uc->wme2qid[WME_AC_BE] = RTWN_BULK_TX_VI; + uc->wme2qid[WME_AC_BK] = RTWN_BULK_TX_VI; + uc->wme2qid[WME_AC_VI] = RTWN_BULK_TX_VO; break; case 1: rtwn_config[RTWN_BULK_TX_BE].endpoint = addr[0]; rtwn_config[RTWN_BULK_TX_BK].endpoint = addr[0]; rtwn_config[RTWN_BULK_TX_VI].endpoint = addr[0]; + + uc->wme2qid[WME_AC_BE] = RTWN_BULK_TX_VO; + uc->wme2qid[WME_AC_BK] = RTWN_BULK_TX_VO; + uc->wme2qid[WME_AC_VI] = RTWN_BULK_TX_VO; break; default: KASSERT(0, ("unhandled number of endpoints %d\n", uc->ntx)); @@ -225,7 +236,7 @@ rtwn_usb_setup_endpoints(struct rtwn_usb_softc *uc) rtwn_config[RTWN_BULK_RX].bufsize = uc->uc_rx_buf_size * RTWN_USB_RXBUFSZ_UNIT; error = usbd_transfer_setup(uc->uc_udev, &iface_index, - uc->uc_xfer, rtwn_config, RTWN_N_TRANSFER, uc, &sc->sc_mtx); + uc->uc_xfer, rtwn_config, RTWN_BULK_EP_COUNT, uc, &sc->sc_mtx); free(rtwn_config, M_TEMP); if (error) { diff --git a/sys/dev/rtwn/usb/rtwn_usb_tx.c b/sys/dev/rtwn/usb/rtwn_usb_tx.c index 0fb8632d9a16..86d41ed10d91 100644 --- a/sys/dev/rtwn/usb/rtwn_usb_tx.c +++ b/sys/dev/rtwn/usb/rtwn_usb_tx.c @@ -65,10 +65,6 @@ static struct rtwn_data * rtwn_usb_getbuf(struct rtwn_usb_softc *); static void rtwn_usb_txeof(struct rtwn_usb_softc *, struct rtwn_data *, int); -static const uint8_t wme2qid[] = - { RTWN_BULK_TX_BE, RTWN_BULK_TX_BK, - RTWN_BULK_TX_VI, RTWN_BULK_TX_VO }; - static struct rtwn_data * _rtwn_usb_getbuf(struct rtwn_usb_softc *uc) { @@ -105,6 +101,7 @@ static void rtwn_usb_txeof(struct rtwn_usb_softc *uc, struct rtwn_data *data, int status) { struct rtwn_softc *sc = &uc->uc_sc; + bool is_empty = true; RTWN_ASSERT_LOCKED(sc); @@ -120,42 +117,54 @@ rtwn_usb_txeof(struct rtwn_usb_softc *uc, struct rtwn_data *data, int status) STAILQ_INSERT_TAIL(&uc->uc_tx_inactive, data, next); sc->qfullmsk = 0; + #ifndef D4054 - if (STAILQ_EMPTY(&uc->uc_tx_active) && STAILQ_EMPTY(&uc->uc_tx_pending)) + for (int i = RTWN_BULK_TX_FIRST; i < RTWN_BULK_EP_COUNT; i++) { + if (!STAILQ_EMPTY(&uc->uc_tx_active[i]) || + !STAILQ_EMPTY(&uc->uc_tx_pending[i])) + is_empty = false; + } + + if (is_empty) sc->sc_tx_timer = 0; else sc->sc_tx_timer = 5; #endif } -void -rtwn_bulk_tx_callback(struct usb_xfer *xfer, usb_error_t error) +static void +rtwn_bulk_tx_callback_qid(struct usb_xfer *xfer, usb_error_t error, int qid) { struct rtwn_usb_softc *uc = usbd_xfer_softc(xfer); struct rtwn_softc *sc = &uc->uc_sc; struct rtwn_data *data; + bool do_is_empty_check = false; + int i; + + RTWN_DPRINTF(sc, RTWN_DEBUG_XMIT, + "%s: called, qid=%d\n", __func__, qid); RTWN_ASSERT_LOCKED(sc); switch (USB_GET_STATE(xfer)){ case USB_ST_TRANSFERRED: - data = STAILQ_FIRST(&uc->uc_tx_active); + data = STAILQ_FIRST(&uc->uc_tx_active[qid]); if (data == NULL) goto tr_setup; - STAILQ_REMOVE_HEAD(&uc->uc_tx_active, next); + STAILQ_REMOVE_HEAD(&uc->uc_tx_active[qid], next); rtwn_usb_txeof(uc, data, 0); /* FALLTHROUGH */ case USB_ST_SETUP: tr_setup: - data = STAILQ_FIRST(&uc->uc_tx_pending); + data = STAILQ_FIRST(&uc->uc_tx_pending[qid]); if (data == NULL) { RTWN_DPRINTF(sc, RTWN_DEBUG_XMIT, "%s: empty pending queue\n", __func__); - sc->sc_tx_n_active = 0; + do_is_empty_check = true; goto finish; } - STAILQ_REMOVE_HEAD(&uc->uc_tx_pending, next); - STAILQ_INSERT_TAIL(&uc->uc_tx_active, data, next); + STAILQ_REMOVE_HEAD(&uc->uc_tx_pending[qid], next); + STAILQ_INSERT_TAIL(&uc->uc_tx_active[qid], data, next); /* * Note: if this is a beacon frame, ensure that it will go @@ -169,11 +178,17 @@ rtwn_bulk_tx_callback(struct usb_xfer *xfer, usb_error_t error) sc->sc_tx_n_active++; break; default: - data = STAILQ_FIRST(&uc->uc_tx_active); + data = STAILQ_FIRST(&uc->uc_tx_active[qid]); if (data == NULL) goto tr_setup; - STAILQ_REMOVE_HEAD(&uc->uc_tx_active, next); + STAILQ_REMOVE_HEAD(&uc->uc_tx_active[qid], next); rtwn_usb_txeof(uc, data, 1); + if (error != 0) + device_printf(sc->sc_dev, + "%s: called; txeof qid=%d, error=%s\n", + __func__, + qid, + usbd_errstr(error)); if (error != USB_ERR_CANCELLED) { usbd_xfer_set_stall(xfer); goto tr_setup; @@ -181,6 +196,19 @@ rtwn_bulk_tx_callback(struct usb_xfer *xfer, usb_error_t error) break; } finish: + + /* + * Clear sc_tx_n_active if all the pending transfers are 0. + * + * This is currently a crutch because net80211 doesn't provide + * a way to defer all the FF checks or one of the FF checks. + * Eventually this should just be tracked per-endpoint. + */ + for (i = RTWN_BULK_TX_FIRST; i < RTWN_BULK_EP_COUNT; i++) + if (STAILQ_FIRST(&uc->uc_tx_pending[i]) != NULL) + do_is_empty_check = false; + if (do_is_empty_check) + sc->sc_tx_n_active = 0; #ifdef IEEE80211_SUPPORT_SUPERG /* * If the TX active queue drops below a certain @@ -210,6 +238,34 @@ rtwn_bulk_tx_callback(struct usb_xfer *xfer, usb_error_t error) rtwn_start(sc); } +void +rtwn_bulk_tx_callback_be(struct usb_xfer *xfer, usb_error_t error) +{ + + rtwn_bulk_tx_callback_qid(xfer, error, RTWN_BULK_TX_BE); +} + +void +rtwn_bulk_tx_callback_bk(struct usb_xfer *xfer, usb_error_t error) +{ + + rtwn_bulk_tx_callback_qid(xfer, error, RTWN_BULK_TX_BK); +} + +void +rtwn_bulk_tx_callback_vi(struct usb_xfer *xfer, usb_error_t error) +{ + + rtwn_bulk_tx_callback_qid(xfer, error, RTWN_BULK_TX_VI); +} + +void +rtwn_bulk_tx_callback_vo(struct usb_xfer *xfer, usb_error_t error) +{ + + rtwn_bulk_tx_callback_qid(xfer, error, RTWN_BULK_TX_VO); +} + static void rtwn_usb_tx_checksum(struct rtwn_tx_desc_common *txd) { @@ -226,6 +282,7 @@ rtwn_usb_tx_start(struct rtwn_softc *sc, struct ieee80211_node *ni, struct rtwn_data *data; struct usb_xfer *xfer; uint16_t ac; + int qid = 0; RTWN_ASSERT_LOCKED(sc); @@ -236,17 +293,23 @@ rtwn_usb_tx_start(struct rtwn_softc *sc, struct ieee80211_node *ni, if (data == NULL) return (ENOBUFS); + /* TODO: should really get a consistent AC/TID, ath(4) style */ ac = M_WME_GETAC(m); switch (type) { case IEEE80211_FC0_TYPE_CTL: case IEEE80211_FC0_TYPE_MGT: - xfer = uc->uc_xfer[RTWN_BULK_TX_VO]; + qid = RTWN_BULK_TX_VO; break; default: - xfer = uc->uc_xfer[wme2qid[ac]]; + qid = uc->wme2qid[ac]; break; } + xfer = uc->uc_xfer[qid]; + + RTWN_DPRINTF(sc, RTWN_DEBUG_XMIT, + "%s: called, ac=%d, qid=%d, xfer=%p\n", + __func__, ac, qid, xfer); txd = (struct rtwn_tx_desc_common *)tx_desc; txd->pktlen = htole16(m->m_pkthdr.len); @@ -264,6 +327,7 @@ rtwn_usb_tx_start(struct rtwn_softc *sc, struct ieee80211_node *ni, data->buflen = m->m_pkthdr.len + sc->txdesc_len; data->id = id; data->ni = ni; + data->qid = qid; if (data->ni != NULL) { data->m = m; #ifndef D4054 @@ -271,7 +335,7 @@ rtwn_usb_tx_start(struct rtwn_softc *sc, struct ieee80211_node *ni, #endif } - STAILQ_INSERT_TAIL(&uc->uc_tx_pending, data, next); + STAILQ_INSERT_TAIL(&uc->uc_tx_pending[qid], data, next); if (STAILQ_EMPTY(&uc->uc_tx_inactive)) sc->qfullmsk = 1; diff --git a/sys/dev/rtwn/usb/rtwn_usb_tx.h b/sys/dev/rtwn/usb/rtwn_usb_tx.h index 7b762cc01a00..193103f32707 100644 --- a/sys/dev/rtwn/usb/rtwn_usb_tx.h +++ b/sys/dev/rtwn/usb/rtwn_usb_tx.h @@ -17,7 +17,10 @@ #ifndef RTWN_USB_TX_H #define RTWN_USB_TX_H -void rtwn_bulk_tx_callback(struct usb_xfer *, usb_error_t); +void rtwn_bulk_tx_callback_bk(struct usb_xfer *, usb_error_t); +void rtwn_bulk_tx_callback_be(struct usb_xfer *, usb_error_t); +void rtwn_bulk_tx_callback_vi(struct usb_xfer *, usb_error_t); +void rtwn_bulk_tx_callback_vo(struct usb_xfer *, usb_error_t); int rtwn_usb_tx_start(struct rtwn_softc *, struct ieee80211_node *, struct mbuf *, uint8_t *, uint8_t, int); diff --git a/sys/dev/rtwn/usb/rtwn_usb_var.h b/sys/dev/rtwn/usb/rtwn_usb_var.h index b67cf8ca3af6..d2970ae4381f 100644 --- a/sys/dev/rtwn/usb/rtwn_usb_var.h +++ b/sys/dev/rtwn/usb/rtwn_usb_var.h @@ -37,6 +37,7 @@ struct rtwn_data { uint8_t *buf; /* 'id' is meaningful for beacons only */ int id; + int qid; uint16_t buflen; struct mbuf *m; struct ieee80211_node *ni; @@ -50,15 +51,16 @@ enum { RTWN_BULK_TX_BK, /* = WME_AC_BK */ RTWN_BULK_TX_VI, /* = WME_AC_VI */ RTWN_BULK_TX_VO, /* = WME_AC_VO */ - RTWN_N_TRANSFER = 5, + RTWN_BULK_EP_COUNT = 5, }; #define RTWN_EP_QUEUES RTWN_BULK_RX +#define RTWN_BULK_TX_FIRST RTWN_BULK_TX_BE struct rtwn_usb_softc { struct rtwn_softc uc_sc __subobject_member_used_for_c_inheritance; /* must be the first */ struct usb_device *uc_udev; - struct usb_xfer *uc_xfer[RTWN_N_TRANSFER]; + struct usb_xfer *uc_xfer[RTWN_BULK_EP_COUNT]; struct rtwn_data uc_rx[RTWN_USB_RX_LIST_COUNT]; rtwn_datahead uc_rx_active; @@ -70,14 +72,16 @@ struct rtwn_usb_softc { int uc_rx_off; struct rtwn_data uc_tx[RTWN_USB_TX_LIST_COUNT]; - rtwn_datahead uc_tx_active; + rtwn_datahead uc_tx_active[RTWN_BULK_EP_COUNT]; rtwn_datahead uc_tx_inactive; - rtwn_datahead uc_tx_pending; + rtwn_datahead uc_tx_pending[RTWN_BULK_EP_COUNT]; int (*uc_align_rx)(int, int); int ntx; int tx_agg_desc_num; + + uint8_t wme2qid[4]; }; #define RTWN_USB_SOFTC(sc) ((struct rtwn_usb_softc *)(sc)) From c7bb8a1bb50539c090171755079baba0ed587537 Mon Sep 17 00:00:00 2001 From: Adrian Chadd Date: Tue, 26 Nov 2024 16:59:15 -0800 Subject: [PATCH 05/14] rtwn: enable FCS in the recive config to work around truncated frames I noticed that on RTL8812AU/RTL8821AU receiving VHT frames that I'd occasionally see frames missing the last 4 bytes. I can easily reproduce it with a ping sweep and fast (10ms) between frames. There's also a report of an earlier NIC (RTL8188EU) doing the same thing with HT frames but not with OFDM (11g) frames. After a bunch of poking, it turns out a driver where things DID work properly for the other report kept FCS enabled, and trimmed it from the frame before pushing it up to the network layer. I did the same and it also worked fine. The other solution was to disable PHYSTATUS notifications, but then we'd get no per packet RX notifications (RX rate, RSSI, etc.) Locally tested: * RTL8192EU, STA mode (HT) * RTL8812AU, STA mode (HT, VHT) * RTL8821AU, STA mode (HT, VHT) Differential Revision: https://reviews.freebsd.org/D47775 (cherry picked from commit d76247e801dec95600a068fa1bb09f4f57e00031) --- sys/dev/rtwn/if_rtwn_rx.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/sys/dev/rtwn/if_rtwn_rx.c b/sys/dev/rtwn/if_rtwn_rx.c index 762472eca440..ebfb8e67ec6d 100644 --- a/sys/dev/rtwn/if_rtwn_rx.c +++ b/sys/dev/rtwn/if_rtwn_rx.c @@ -318,6 +318,10 @@ rtwn_rx_common(struct rtwn_softc *sc, struct mbuf *m, void *desc) /* Drop PHY descriptor. */ m_adj(m, infosz + shift); + /* If APPFCS, drop FCS */ + if (sc->rcr & R92C_RCR_APPFCS) + m_adj(m, -IEEE80211_CRC_LEN); + return (ni); } @@ -456,6 +460,15 @@ rtwn_rxfilter_init(struct rtwn_softc *sc) R92C_RCR_HTC_LOC_CTRL | R92C_RCR_APP_PHYSTS | R92C_RCR_APP_ICV | R92C_RCR_APP_MIC; + /* + * Add FCS, to work around occasional 4 byte truncation + * with some frames. This is more problematic on RTL8812/ + * RTL8821 because they're also doing L3/L4 checksum offload + * and hardware encryption, so both are tagged as "passed" + * before the frame is truncated. + */ + sc->rcr |= R92C_RCR_APPFCS; + /* Update dynamic Rx filter parts. */ rtwn_rxfilter_update(sc); } @@ -487,7 +500,7 @@ rtwn_set_promisc(struct rtwn_softc *sc) RTWN_ASSERT_LOCKED(sc); mask_all = R92C_RCR_ACF | R92C_RCR_ADF | R92C_RCR_AMF | R92C_RCR_AAP; - mask_min = R92C_RCR_APM; + mask_min = R92C_RCR_APM | R92C_RCR_APPFCS; if (sc->bcn_vaps == 0) mask_min |= R92C_RCR_CBSSID_BCN; From 8e6c87121cd91ceea39b755e199feb956164a866 Mon Sep 17 00:00:00 2001 From: Adrian Chadd Date: Sun, 1 Dec 2024 21:22:45 -0800 Subject: [PATCH 06/14] rtwn: don't do 64 bit TSF extension by default The TSF64 extension involves at least 3 reads from TSF registers (R92C_TSFTR(0), R92C_TSFTR(1), R92C_TSFTR(2)) which are 4 byte control transfers. They take up valuable USB link time. It's likely much less expensive for PCIe adapters. At some point it may be worthwhile enabling it by default just for those. With this disabled, the only USB traffic that I see during normal data operation are bulk TX/RX data transfers for 802.11 packets, and on NICs w/ net80211 rate control, the control register space read/writes for TX completion. (And that will also need addressing.) This is the difference between 15mbit TCP RX and 30mbit TCP RX on the 11n NICs, and around 40 to 50mbit TCP RX on the 11ac NICs in HT40 and VHT80. Locally tested: * RTL8188EU, STA mode * RTL8192CU, STA mode * RTL8192EU, STA mode * RTL8811AU, STA mode * RTL8821AU, STA mode Differential Revision: https://reviews.freebsd.org/D47861 (cherry picked from commit fcb5e8d0c19ac21515ab3047d39a76b32d835cec) --- sys/dev/rtwn/if_rtwn.c | 5 +++++ sys/dev/rtwn/if_rtwn_rx.c | 14 ++++++++++++-- sys/dev/rtwn/if_rtwnvar.h | 1 + 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/sys/dev/rtwn/if_rtwn.c b/sys/dev/rtwn/if_rtwn.c index 4334d5700e51..6de5f6696879 100644 --- a/sys/dev/rtwn/if_rtwn.c +++ b/sys/dev/rtwn/if_rtwn.c @@ -332,6 +332,11 @@ rtwn_sysctlattach(struct rtwn_softc *sc) sc->sc_ht40, "Enable 40 MHz mode support"); #endif + sc->sc_ena_tsf64 = 0; + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, + "ena_tsf64", CTLFLAG_RWTUN, &sc->sc_ena_tsf64, + sc->sc_ena_tsf64, "Enable/disable per-packet TSF64 reporting"); + #ifdef RTWN_DEBUG SYSCTL_ADD_U32(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "debug", CTLFLAG_RWTUN, &sc->sc_debug, sc->sc_debug, diff --git a/sys/dev/rtwn/if_rtwn_rx.c b/sys/dev/rtwn/if_rtwn_rx.c index ebfb8e67ec6d..58cd53b01e63 100644 --- a/sys/dev/rtwn/if_rtwn_rx.c +++ b/sys/dev/rtwn/if_rtwn_rx.c @@ -285,8 +285,18 @@ rtwn_rx_common(struct rtwn_softc *sc, struct mbuf *m, void *desc) rxs.c_pktflags |= IEEE80211_RX_F_FAIL_FCSCRC; rxs.r_flags |= IEEE80211_R_TSF_START; /* XXX undocumented */ - rxs.r_flags |= IEEE80211_R_TSF64; - rxs.c_rx_tsf = rtwn_extend_rx_tsf(sc, stat); + + /* + * Doing the TSF64 extension on USB is expensive, especially + * if it's being done on every MPDU in an AMPDU burst. + */ + if (sc->sc_ena_tsf64) { + rxs.r_flags |= IEEE80211_R_TSF64; + rxs.c_rx_tsf = rtwn_extend_rx_tsf(sc, stat); + } else { + rxs.r_flags |= IEEE80211_R_TSF32; + rxs.c_rx_tsf = le32toh(stat->tsf_low); + } /* Get RSSI from PHY status descriptor. */ is_cck = (rxs.c_pktflags & IEEE80211_RX_F_CCK) != 0; diff --git a/sys/dev/rtwn/if_rtwnvar.h b/sys/dev/rtwn/if_rtwnvar.h index f4c6d7ee64b4..1602992f7f75 100644 --- a/sys/dev/rtwn/if_rtwnvar.h +++ b/sys/dev/rtwn/if_rtwnvar.h @@ -174,6 +174,7 @@ struct rtwn_softc { #if 1 int sc_ht40; #endif + int sc_ena_tsf64; uint32_t sc_debug; int sc_hwcrypto; int sc_ratectl_sysctl; From 090c555b85764cd8b2a8f799267ee49764c0384e Mon Sep 17 00:00:00 2001 From: Adrian Chadd Date: Mon, 16 Dec 2024 12:09:56 -0800 Subject: [PATCH 07/14] rtwn: make sure RCR_APPFCS stays set in monitor mode / mode changes. My previous commit meant that APPFCS wasn't enabled during monitor mode and likely other corner cases. Ensure it stays on at all times. This, amusingly, fixes monitor mode in RTL8812AU/RTL8821AU - without it, I don't see HT/VHT frames in monitor mode but I can still receive them in normal STA mode. Differential Revision: https://reviews.freebsd.org/D48112 (cherry picked from commit 791170aaf7efb4e053ccbf537d80a43e8a81d1e4) --- sys/dev/rtwn/if_rtwn_rx.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/sys/dev/rtwn/if_rtwn_rx.c b/sys/dev/rtwn/if_rtwn_rx.c index 58cd53b01e63..fa1440647ef1 100644 --- a/sys/dev/rtwn/if_rtwn_rx.c +++ b/sys/dev/rtwn/if_rtwn_rx.c @@ -510,7 +510,7 @@ rtwn_set_promisc(struct rtwn_softc *sc) RTWN_ASSERT_LOCKED(sc); mask_all = R92C_RCR_ACF | R92C_RCR_ADF | R92C_RCR_AMF | R92C_RCR_AAP; - mask_min = R92C_RCR_APM | R92C_RCR_APPFCS; + mask_min = R92C_RCR_APM; if (sc->bcn_vaps == 0) mask_min |= R92C_RCR_CBSSID_BCN; @@ -529,5 +529,12 @@ rtwn_set_promisc(struct rtwn_softc *sc) sc->rcr &= ~mask_min; sc->rcr |= mask_all; } + + /* + * Add FCS, to work around occasional 4 byte truncation. + * See the previous comment above R92C_RCR_APPFCS. + */ + sc->rcr |= R92C_RCR_APPFCS; + rtwn_rxfilter_set(sc); } From 4e22e93e3eb30d043905a47204d70b742e46d564 Mon Sep 17 00:00:00 2001 From: "Bjoern A. Zeeb" Date: Mon, 27 Jan 2025 13:54:02 +0000 Subject: [PATCH 08/14] net80211: crypto: ccmp: fix more hardware offload bits Add the missing IEEE80211_RX_F_DECRYPTED and IEEE80211_RX_F_MMIC_STRIP (really just MIC_STRIP) checks to make hwaccel offload work. This makes rtw8x drivers pass RX packets again at least with LinuxKPI if HW_CRYPTO support is enabled. Sponsored by: The FreeBSD Foundation MFC after: 3 days Reviewed by: adrian Differential Revision: https://reviews.freebsd.org/D49030 (cherry picked from commit 3afc0bfecb1a927c37672dc245688c575e4d9ec4) --- sys/net80211/ieee80211_crypto_ccmp.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/sys/net80211/ieee80211_crypto_ccmp.c b/sys/net80211/ieee80211_crypto_ccmp.c index a54a8d40c81a..f6a92f08d943 100644 --- a/sys/net80211/ieee80211_crypto_ccmp.c +++ b/sys/net80211/ieee80211_crypto_ccmp.c @@ -593,6 +593,7 @@ ccmp_encrypt(struct ieee80211_key *key, struct mbuf *m0, int hdrlen) static int ccmp_decrypt(struct ieee80211_key *key, u_int64_t pn, struct mbuf *m, int hdrlen) { + const struct ieee80211_rx_stats *rxs; struct ccmp_ctx *ctx = key->wk_private; struct ieee80211vap *vap = ctx->cc_vap; struct ieee80211_frame *wh; @@ -604,6 +605,10 @@ ccmp_decrypt(struct ieee80211_key *key, u_int64_t pn, struct mbuf *m, int hdrlen uint8_t *pos; u_int space; + rxs = ieee80211_get_rx_params_ptr(m); + if ((rxs != NULL) && (rxs->c_pktflags & IEEE80211_RX_F_DECRYPTED) != 0) + return (1); + ctx->cc_vap->iv_stats.is_crypto_ccmp++; wh = mtod(m, struct ieee80211_frame *); @@ -666,6 +671,14 @@ ccmp_decrypt(struct ieee80211_key *key, u_int64_t pn, struct mbuf *m, int hdrlen space = m->m_len; } } + + /* + * If the MIC (we use MMIC despite not being Micheal) was stripped + * by HW/driver we are done. + */ + if ((rxs != NULL) && (rxs->c_pktflags & IEEE80211_RX_F_MMIC_STRIP) != 0) + return (1); + if (memcmp(mic, a, ccmp.ic_trailer) != 0) { IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO, wh->i_addr2, "%s", "AES-CCM decrypt failed; MIC mismatch"); From dfa53054c506b011f91b79c36a973390b6d003f7 Mon Sep 17 00:00:00 2001 From: Adrian Chadd Date: Tue, 1 Apr 2025 22:18:37 +0100 Subject: [PATCH 09/14] net80211: validate control frame TA/RA before further processing Summary: An earlier commit relaxed the TA/RA rules around control frames to fix other issues, however it now results in control frames not specifically from a known node / to us to be handled in the control path. Specifically, rtwn(4) RTL8812/RTL8821 NICs are currently passing BARs from the AP TA to any destination to us; which is tripping up BAW tracking and causing traffic hangs. Reviewers: #wireless, jrtc27, bz Subscribers: bz, imp, melifaro, glebius Tags: #wireless Differential Revision: https://reviews.freebsd.org/D49575 --- sys/net80211/ieee80211.c | 54 +++++++++++++++++++++++++++++++++ sys/net80211/ieee80211_adhoc.c | 3 +- sys/net80211/ieee80211_hostap.c | 3 +- sys/net80211/ieee80211_sta.c | 4 ++- sys/net80211/ieee80211_var.h | 3 ++ 5 files changed, 64 insertions(+), 3 deletions(-) diff --git a/sys/net80211/ieee80211.c b/sys/net80211/ieee80211.c index ecf87020b066..f880ac92fcef 100644 --- a/sys/net80211/ieee80211.c +++ b/sys/net80211/ieee80211.c @@ -2710,3 +2710,57 @@ ieee80211_is_key_unicast(const struct ieee80211vap *vap, */ return (!ieee80211_is_key_global(vap, key)); } + +/** + * Determine whether the given control frame is from a known node + * and destined to us. + * + * In some instances a control frame won't have a TA (eg ACKs), so + * we should only verify the RA for those. + * + * @param ni ieee80211_node representing the sender, or BSS node + * @param m0 mbuf representing the 802.11 frame. + * @returns false if the frame is not a CTL frame (with a warning logged); + * true if the frame is from a known sender / valid recipient, + * false otherwise. + */ +bool +ieee80211_is_ctl_frame_for_vap(struct ieee80211_node *ni, const struct mbuf *m0) +{ + const struct ieee80211vap *vap = ni->ni_vap; + const struct ieee80211_frame *wh; + uint8_t subtype; + + wh = mtod(m0, const struct ieee80211_frame *); + subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK; + + /* Verify it's a ctl frame. */ + KASSERT(IEEE80211_IS_CTL(wh), ("%s: not a CTL frame (fc[0]=0x%04x)", + __func__, wh->i_fc[0])); + if (!IEEE80211_IS_CTL(wh)) { + if_printf(vap->iv_ifp, + "%s: not a control frame (fc[0]=0x%04x)\n", + __func__, wh->i_fc[0]); + return (false); + } + + /* Verify the TA if present. */ + switch (subtype) { + case IEEE80211_FC0_SUBTYPE_CTS: + case IEEE80211_FC0_SUBTYPE_ACK: + /* No TA. */ + break; + default: + /* + * Verify TA matches ni->ni_macaddr; for unknown + * sources it will be the BSS node and ni->ni_macaddr + * will the BSS MAC. + */ + if (!IEEE80211_ADDR_EQ(wh->i_addr2, ni->ni_macaddr)) + return (false); + break; + } + + /* Verify the RA */ + return (IEEE80211_ADDR_EQ(wh->i_addr1, vap->iv_myaddr)); +} diff --git a/sys/net80211/ieee80211_adhoc.c b/sys/net80211/ieee80211_adhoc.c index f5e8a301ad28..aa13aeb3a988 100644 --- a/sys/net80211/ieee80211_adhoc.c +++ b/sys/net80211/ieee80211_adhoc.c @@ -663,7 +663,8 @@ adhoc_input(struct ieee80211_node *ni, struct mbuf *m, case IEEE80211_FC0_TYPE_CTL: vap->iv_stats.is_rx_ctl++; IEEE80211_NODE_STAT(ni, rx_ctrl); - vap->iv_recv_ctl(ni, m, subtype); + if (ieee80211_is_ctl_frame_for_vap(ni, m)) + vap->iv_recv_ctl(ni, m, subtype); goto out; default: diff --git a/sys/net80211/ieee80211_hostap.c b/sys/net80211/ieee80211_hostap.c index ac97889a9cef..66e47a557e73 100644 --- a/sys/net80211/ieee80211_hostap.c +++ b/sys/net80211/ieee80211_hostap.c @@ -893,7 +893,8 @@ hostap_input(struct ieee80211_node *ni, struct mbuf *m, case IEEE80211_FC0_TYPE_CTL: vap->iv_stats.is_rx_ctl++; IEEE80211_NODE_STAT(ni, rx_ctrl); - vap->iv_recv_ctl(ni, m, subtype); + if (ieee80211_is_ctl_frame_for_vap(ni, m)) + vap->iv_recv_ctl(ni, m, subtype); goto out; default: IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY, diff --git a/sys/net80211/ieee80211_sta.c b/sys/net80211/ieee80211_sta.c index 8fd4de162359..2b5042cf262a 100644 --- a/sys/net80211/ieee80211_sta.c +++ b/sys/net80211/ieee80211_sta.c @@ -976,7 +976,8 @@ sta_input(struct ieee80211_node *ni, struct mbuf *m, case IEEE80211_FC0_TYPE_CTL: vap->iv_stats.is_rx_ctl++; IEEE80211_NODE_STAT(ni, rx_ctrl); - vap->iv_recv_ctl(ni, m, subtype); + if (ieee80211_is_ctl_frame_for_vap(ni, m)) + vap->iv_recv_ctl(ni, m, subtype); goto out; default: @@ -2058,6 +2059,7 @@ sta_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0, int subtype, static void sta_recv_ctl(struct ieee80211_node *ni, struct mbuf *m, int subtype) { + switch (subtype) { case IEEE80211_FC0_SUBTYPE_BAR: ieee80211_recv_bar(ni, m); diff --git a/sys/net80211/ieee80211_var.h b/sys/net80211/ieee80211_var.h index 53139a3852b8..67dad31d3523 100644 --- a/sys/net80211/ieee80211_var.h +++ b/sys/net80211/ieee80211_var.h @@ -829,6 +829,9 @@ bool ieee80211_is_key_global(const struct ieee80211vap *vap, bool ieee80211_is_key_unicast(const struct ieee80211vap *vap, const struct ieee80211_key *key); +bool ieee80211_is_ctl_frame_for_vap(struct ieee80211_node *, + const struct mbuf *); + void ieee80211_radiotap_attach(struct ieee80211com *, struct ieee80211_radiotap_header *th, int tlen, uint32_t tx_radiotap, From 2149a9f2cbfcc3348b75ea957a1bf84aceed8471 Mon Sep 17 00:00:00 2001 From: Adrian Chadd Date: Tue, 1 Apr 2025 22:19:33 +0100 Subject: [PATCH 10/14] rtwn: enable reception of BAR frames Summary: The RX filter wasn't enabling BAR frames, so we weren't receiving them during normal operation. Jessica noticed we WERE getting BAR frames, but only when promisc mode is active. Which is a different set of bugs, but it did highlight the differences here. PR: kern/285822 Reviewers: #wireless, jrtc27 Subscribers: imp Tags: #wireless Differential Revision: https://reviews.freebsd.org/D49596 --- sys/dev/rtwn/if_rtwn.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/sys/dev/rtwn/if_rtwn.c b/sys/dev/rtwn/if_rtwn.c index 6de5f6696879..45fc3b97dd27 100644 --- a/sys/dev/rtwn/if_rtwn.c +++ b/sys/dev/rtwn/if_rtwn.c @@ -1129,6 +1129,9 @@ rtwn_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) /* Stop Rx of data frames. */ rtwn_write_2(sc, R92C_RXFLTMAP2, 0); + /* Stop Rx of control frames. */ + rtwn_write_2(sc, R92C_RXFLTMAP1, 0); + /* Reset EDCA parameters. */ rtwn_write_4(sc, R92C_EDCA_VO_PARAM, 0x002f3217); rtwn_write_4(sc, R92C_EDCA_VI_PARAM, 0x005e4317); @@ -1267,6 +1270,11 @@ rtwn_run(struct rtwn_softc *sc, struct ieee80211vap *vap) rtwn_write_2(sc, R92C_BCN_INTERVAL(uvp->id), ni->ni_intval); if (sc->vaps_running == sc->monvaps_running) { + /* Enable Rx of BAR control frames. */ + rtwn_write_2(sc, R92C_RXFLTMAP1, + 1 << (IEEE80211_FC0_SUBTYPE_BAR >> + IEEE80211_FC0_SUBTYPE_SHIFT)); + /* Enable Rx of data frames. */ rtwn_write_2(sc, R92C_RXFLTMAP2, 0xffff); From a4ac309a834bfbb04687b48f8a643253fd75e15c Mon Sep 17 00:00:00 2001 From: Jessica Clarke Date: Tue, 1 Apr 2025 22:21:55 +0100 Subject: [PATCH 11/14] rtwn: Disable IP/TCP/UDP checksum offloading I have consistently seen false-positive checksum failures for UDP/IP packets with a missing, i.e. zero, checksum (specifically, for packets from the UniOfCam-IoT network's DHCP server), but valid checksums reported for UDP/IP packets with a non-zero checksum. Therefore I believe that the hardware does not correctly handle the optional nature of UDP/IP's checksums, so ignore the result and let the network stack validate the checksum itself. Neither Linux nor OpenBSD seems to use this feature that I can see. Whilst there is the old Linux vendor driver that defines macros for these specific fields, it too does not check them. --- sys/dev/rtwn/rtl8812a/r12a_rx.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/sys/dev/rtwn/rtl8812a/r12a_rx.c b/sys/dev/rtwn/rtl8812a/r12a_rx.c index 763397636ac9..b91f251856ce 100644 --- a/sys/dev/rtwn/rtl8812a/r12a_rx.c +++ b/sys/dev/rtwn/rtl8812a/r12a_rx.c @@ -178,6 +178,15 @@ r12a_handle_c2h_report(struct rtwn_softc *sc, uint8_t *buf, int len) int r12a_check_frame_checksum(struct rtwn_softc *sc, struct mbuf *m) { + /* + * XXX: RTL8821AU seems to incorrectly handle at least UDP checksum + * fields of 0, treating them as an error. Ignore the offloaded + * checksumming result until this has been better characterised + * (including whether all-ones is correctly handled). + * + * https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=285837 + */ +#if 0 struct r12a_softc *rs = sc->sc_priv; struct r92c_rx_stat *stat; uint32_t rxdw1; @@ -202,6 +211,7 @@ r12a_check_frame_checksum(struct rtwn_softc *sc, struct mbuf *m) m->m_pkthdr.csum_data = 0xffff; } } +#endif return (0); } From bae367d8f795ccd0520bf6fb56fcdafa7d9f5816 Mon Sep 17 00:00:00 2001 From: Jessica Clarke Date: Fri, 28 Mar 2025 18:22:29 +0000 Subject: [PATCH 12/14] rtwn: Add missing subobject bounds annotations 65fdef2b64f4 ("rtwn: Add subobject bounds annotations for softc subclasses.") caught some of these but not all. This allows rtwn to attach to a TP-Link Archer T2U Nano (RTL8821AU). --- sys/dev/rtwn/if_rtwnvar.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sys/dev/rtwn/if_rtwnvar.h b/sys/dev/rtwn/if_rtwnvar.h index 1602992f7f75..981a60d6460a 100644 --- a/sys/dev/rtwn/if_rtwnvar.h +++ b/sys/dev/rtwn/if_rtwnvar.h @@ -96,7 +96,7 @@ struct rtwn_cmdq { #define RTWN_CMDQ_SIZE 16 struct rtwn_node { - struct ieee80211_node ni; /* must be the first */ + struct ieee80211_node ni __subobject_member_used_for_c_inheritance; /* must be the first */ int id; struct rtwn_tx_phystat last_physt; @@ -105,7 +105,7 @@ struct rtwn_node { #define RTWN_NODE(ni) ((struct rtwn_node *)(ni)) struct rtwn_vap { - struct ieee80211vap vap; + struct ieee80211vap vap __subobject_member_used_for_c_inheritance; int id; #define RTWN_VAP_ID_INVALID -1 int curr_mode; From c86c8c6e41f63c4d5eb48c323f208f1905323596 Mon Sep 17 00:00:00 2001 From: Jessica Clarke Date: Fri, 28 Mar 2025 18:25:04 +0000 Subject: [PATCH 13/14] net80211: Don't bound structs that extend radiotap headers These are intended to be embedded, not used standalone, with a pointer to the inner ieee80211_radiotap_header member passed to net80211 functions that is then used to walk off the end of the header into the larger containing struct. Whilst ieee80211_radiotap_vendor_header doesn't appear to be problematic today, it's still intended to be embedded as a header, and it seems likely that a similar access pattern (rather than always being derived from a ieee80211_radiotap_header *) could appear, so keep it consistent with the non-vendor one. --- sys/net80211/ieee80211_radiotap.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sys/net80211/ieee80211_radiotap.h b/sys/net80211/ieee80211_radiotap.h index d44f81b68b8c..d1812e91088b 100644 --- a/sys/net80211/ieee80211_radiotap.h +++ b/sys/net80211/ieee80211_radiotap.h @@ -55,7 +55,7 @@ #define IEEE80211_RADIOTAP_HDRLEN 64 /* XXX deprecated */ -struct ieee80211_radiotap_vendor_header { +struct __no_subobject_bounds ieee80211_radiotap_vendor_header { uint8_t vh_oui[3]; /* 3 byte vendor OUI */ uint8_t vh_sub_ns; /* Sub namespace of this section */ uint16_t vh_skip_len; /* Length of this vendor section */ @@ -66,7 +66,7 @@ struct ieee80211_radiotap_vendor_header { * * Note well: all radiotap fields are little-endian. */ -struct ieee80211_radiotap_header { +struct __no_subobject_bounds ieee80211_radiotap_header { uint8_t it_version; /* Version 0. Only increases * for drastic changes, * introduction of compatible From 2339ee891fe2f9a41730fe01148da380110596da Mon Sep 17 00:00:00 2001 From: Jessica Clarke Date: Fri, 28 Mar 2025 19:18:46 +0000 Subject: [PATCH 14/14] net80211: Fix subobject bounds issue in IEEE80211_IOC_CHANINFO We fault on the kernel access into the array after ic_nchans here which manifests as EFAULT returned back to userspace. --- sys/net80211/ieee80211_ioctl.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sys/net80211/ieee80211_ioctl.c b/sys/net80211/ieee80211_ioctl.c index b53874414822..acdd45081400 100644 --- a/sys/net80211/ieee80211_ioctl.c +++ b/sys/net80211/ieee80211_ioctl.c @@ -147,7 +147,8 @@ ieee80211_ioctl_getchaninfo(struct ieee80211vap *vap, struct ieee80211req *ireq) if (space > ireq->i_len) space = ireq->i_len; /* XXX assumes compatible layout */ - return copyout(&ic->ic_nchans, ireq->i_data, space); + return copyout((char *)ic + __offsetof(struct ieee80211com, ic_nchans), + ireq->i_data, space); } static int