On Thu, May 14, 2026 at 11:27:39PM +0200, Stefan Sperling wrote:
> On Thu, May 14, 2026 at 02:12:00PM -0700, Greg Steuck wrote:
> > I just installed a new snap and it failed to get qwx working in
> > GENERIC.MP upon first boot. I cycled it with ifconfig down/up to no
> > avail. At reboot time I got the splassert above. After the second
> > reboot it works. I include dmesg from both boots as they carry some qwx
> > diagnostics.
>
> Please try the diff below.
>
> It won't fix every problem with this driver but the splasserts will
> hopefully go away.
>
Please also try this larger diff, which rolls in some additional fixes,
partly from mglocker's latest qwz patch.
Again some problems remain but it should be better than the status quo.
M sys/dev/ic/qwx.c | 102+ 90-
M sys/dev/ic/qwxvar.h | 12+ 1-
M sys/dev/pci/if_qwx_pci.c | 8+ 0-
3 files changed, 122 insertions(+), 91 deletions(-)
commit - adef1dd91f575dd2c7b99c729694525977f0fd7c
commit + 2c70921a7f20d264d4a253ea87fca4cd4d4a46c6
blob - 333da499f818b16c992fbff9251463a35b7b7097
blob + ee1d01abcf65e5986e28ce2aeb6e748b07e2bc34
--- sys/dev/ic/qwx.c
+++ sys/dev/ic/qwx.c
@@ -402,6 +402,12 @@ qwx_stop(struct ifnet *ifp)
rw_assert_wrlock(&sc->ioctl_rwl);
+ if (ic->ic_opmode == IEEE80211_M_STA &&
+ ic->ic_state == IEEE80211_S_RUN &&
+ (ic->ic_bss->ni_flags & IEEE80211_NODE_MFP) &&
+ ic->ic_bss->ni_port_valid)
+ qwx_mfp_leave(sc);
+
timeout_del(&sc->mon_reap_timer);
qwx_dp_stop_shadow_timers(sc);
qwx_ce_stop_shadow_timers(sc);
@@ -417,13 +423,6 @@ qwx_stop(struct ifnet *ifp)
qwx_del_task(sc, systq, &sc->bgscan_task);
refcnt_finalize(&sc->task_refs, "qwxstop");
- clear_bit(ATH11K_FLAG_CRASH_FLUSH, sc->sc_flags);
-
- if (ic->ic_opmode == IEEE80211_M_STA &&
- ic->ic_state == IEEE80211_S_RUN &&
- (ic->ic_bss->ni_flags & IEEE80211_NODE_MFP))
- qwx_mfp_leave(sc);
-
qwx_setkey_clear(sc);
ifp->if_timer = sc->sc_tx_timer = 0;
@@ -431,6 +430,8 @@ qwx_stop(struct ifnet *ifp)
ifp->if_flags &= ~IFF_RUNNING;
ifq_clr_oactive(&ifp->if_snd);
+ clear_bit(ATH11K_FLAG_CRASH_FLUSH, sc->sc_flags);
+
/*
* Manually run the newstate task's code for switching to INIT state.
* This reconfigures firmware state to stop scanning, or disassociate
@@ -910,8 +911,20 @@ qwx_add_sta_key(struct qwx_softc *sc, struct ieee80211
nq->flags |= QWX_NODE_FLAG_HAVE_PAIRWISE_KEY;
if ((nq->flags & want_keymask) == want_keymask) {
+ uint8_t pdev_id = 0; /* TODO: derive pdev ID somehow? */
+
DPRINTF("marking port %s valid\n",
ether_sprintf(ni->ni_macaddr));
+
+ /* 4-way handshake done; authorize the BSS peer now. */
+ ret = qwx_wmi_set_peer_param(sc, ni->ni_macaddr, arvif->vdev_id,
+ pdev_id, WMI_PEER_AUTHORIZE, 1);
+ if (ret) {
+ printf("%s: unable to authorize BSS peer: %d\n",
+ sc->sc_dev.dv_xname, ret);
+ return ret;
+ }
+
ni->ni_port_valid = 1;
ieee80211_set_link_state(ic, LINK_STATE_UP);
}
@@ -10994,6 +11007,15 @@ fail_link_desc_cleanup:
}
void
+qwx_dp_rx_tid_clear(struct qwx_softc *sc, struct dp_rx_tid *rx_tid)
+{
+ rx_tid->mem = NULL;
+ rx_tid->vaddr = NULL;
+ rx_tid->paddr = 0ULL;
+ rx_tid->size = 0;
+}
+
+void
qwx_dp_reo_cmd_list_cleanup(struct qwx_softc *sc)
{
struct qwx_dp *dp = &sc->dp;
@@ -11006,13 +11028,7 @@ qwx_dp_reo_cmd_list_cleanup(struct qwx_softc *sc)
TAILQ_FOREACH_SAFE(cmd, &dp->reo_cmd_list, entry, tmp) {
TAILQ_REMOVE(&dp->reo_cmd_list, cmd, entry);
rx_tid = &cmd->data;
- if (rx_tid->mem) {
- qwx_dmamem_free(sc->sc_dmat, rx_tid->mem);
- rx_tid->mem = NULL;
- rx_tid->vaddr = NULL;
- rx_tid->paddr = 0ULL;
- rx_tid->size = 0;
- }
+ qwx_dp_rx_tid_clear(sc, rx_tid);
free(cmd, M_DEVBUF, sizeof(*cmd));
}
@@ -11021,13 +11037,7 @@ qwx_dp_reo_cmd_list_cleanup(struct qwx_softc *sc)
TAILQ_REMOVE(&dp->reo_cmd_cache_flush_list, cmd_cache, entry);
dp->reo_cmd_cache_flush_count--;
rx_tid = &cmd_cache->data;
- if (rx_tid->mem) {
- qwx_dmamem_free(sc->sc_dmat, rx_tid->mem);
- rx_tid->mem = NULL;
- rx_tid->vaddr = NULL;
- rx_tid->paddr = 0ULL;
- rx_tid->size = 0;
- }
+ qwx_dp_rx_tid_clear(sc, rx_tid);
free(cmd_cache, M_DEVBUF, sizeof(*cmd_cache));
}
#ifdef notyet
@@ -11060,6 +11070,13 @@ qwx_dp_free(struct qwx_softc *sc)
dp->tx_ring[i].tx_status = NULL;
}
+ for (i = 0; i <= HAL_DESC_REO_NON_QOS_TID; i++) {
+ if (dp->rx_tid_mem[i]) {
+ qwx_dmamem_free(sc->sc_dmat, dp->rx_tid_mem[i]);
+ dp->rx_tid_mem[i] = NULL;
+ }
+ }
+
/* Deinit any SOC level resource */
}
@@ -14758,7 +14775,11 @@ qwx_peer_map_event(struct qwx_softc *sc, uint8_t vdev_
if (peer == NULL)
return;
- ni = ieee80211_find_node(ic, mac_addr);
+ if (ic->ic_opmode == IEEE80211_M_STA && ic->ic_bss != NULL &&
+ IEEE80211_ADDR_EQ(ic->ic_bss->ni_macaddr, mac_addr))
+ ni = ic->ic_bss;
+ else
+ ni = ieee80211_find_node(ic, mac_addr);
if (ni == NULL)
return;
nq = (struct qwx_node *)ni;
@@ -24516,13 +24537,7 @@ qwx_dp_reo_cmd_free(struct qwx_dp *dp, void *ctx,
printf("%s: failed to flush rx tid hw desc, tid %d status %d\n",
sc->sc_dev.dv_xname, rx_tid->tid, status);
- if (rx_tid->mem) {
- qwx_dmamem_free(sc->sc_dmat, rx_tid->mem);
- rx_tid->mem = NULL;
- rx_tid->vaddr = NULL;
- rx_tid->paddr = 0ULL;
- rx_tid->size = 0;
- }
+ qwx_dp_rx_tid_clear(sc, rx_tid);
}
void
@@ -24557,13 +24572,7 @@ qwx_dp_reo_cache_flush(struct qwx_softc *sc, struct dp
if (ret) {
printf("%s: failed to send HAL_REO_CMD_FLUSH_CACHE cmd, "
"tid %d (%d)\n", sc->sc_dev.dv_xname, rx_tid->tid, ret);
- if (rx_tid->mem) {
- qwx_dmamem_free(sc->sc_dmat, rx_tid->mem);
- rx_tid->mem = NULL;
- rx_tid->vaddr = NULL;
- rx_tid->paddr = 0ULL;
- rx_tid->size = 0;
- }
+ qwx_dp_rx_tid_clear(sc, rx_tid);
}
}
@@ -24577,7 +24586,8 @@ qwx_dp_rx_tid_del_func(struct qwx_dp *dp, void *ctx,
uint64_t now;
if (status == HAL_REO_CMD_DRAIN) {
- goto free_desc;
+ qwx_dp_rx_tid_clear(sc, rx_tid);
+ return;
} else if (status != HAL_REO_CMD_SUCCESS) {
/* Shouldn't happen! Cleanup in case of other failure? */
printf("%s: failed to delete rx tid %d hw descriptor %d\n",
@@ -24586,17 +24596,16 @@ qwx_dp_rx_tid_del_func(struct qwx_dp *dp, void *ctx,
}
elem = malloc(sizeof(*elem), M_DEVBUF, M_ZERO | M_NOWAIT);
- if (!elem)
- goto free_desc;
+ if (!elem) {
+ qwx_dp_rx_tid_clear(sc, rx_tid);
+ return;
+ }
now = getnsecuptime();
elem->ts = now;
memcpy(&elem->data, rx_tid, sizeof(*rx_tid));
- rx_tid->mem = NULL;
- rx_tid->vaddr = NULL;
- rx_tid->paddr = 0ULL;
- rx_tid->size = 0;
+ qwx_dp_rx_tid_clear(sc, rx_tid);
#ifdef notyet
spin_lock_bh(&dp->reo_cmd_lock);
@@ -24623,15 +24632,6 @@ qwx_dp_rx_tid_del_func(struct qwx_dp *dp, void *ctx,
#ifdef notyet
spin_unlock_bh(&dp->reo_cmd_lock);
#endif
- return;
-free_desc:
- if (rx_tid->mem) {
- qwx_dmamem_free(sc->sc_dmat, rx_tid->mem);
- rx_tid->mem = NULL;
- rx_tid->vaddr = NULL;
- rx_tid->paddr = 0ULL;
- rx_tid->size = 0;
- }
}
void
@@ -24660,13 +24660,7 @@ qwx_peer_rx_tid_delete(struct qwx_softc *sc, struct at
sc->sc_dev.dv_xname, tid, ret);
}
- if (rx_tid->mem) {
- qwx_dmamem_free(sc->sc_dmat, rx_tid->mem);
- rx_tid->mem = NULL;
- rx_tid->vaddr = NULL;
- rx_tid->paddr = 0ULL;
- rx_tid->size = 0;
- }
+ qwx_dp_rx_tid_clear(sc, rx_tid);
}
}
@@ -24781,13 +24775,11 @@ qwx_dp_rx_tid_mem_free(struct qwx_softc *sc, struct ie
if (peer) {
rx_tid = &peer->rx_tid[tid];
- if (rx_tid->mem) {
- qwx_dmamem_free(sc->sc_dmat, rx_tid->mem);
- rx_tid->mem = NULL;
- rx_tid->vaddr = NULL;
- rx_tid->paddr = 0ULL;
- rx_tid->size = 0;
- }
+ /*
+ * Nothing to free here since DMA memory pointers are
+ * managed as part of qwx_softc.
+ */
+ qwx_dp_rx_tid_clear(sc, rx_tid);
rx_tid->active = 0;
}
@@ -24801,6 +24793,7 @@ qwx_peer_rx_tid_setup(struct qwx_softc *sc, struct iee
int vdev_id, int pdev_id, uint8_t tid, uint32_t ba_win_sz, uint16_t ssn,
enum hal_pn_type pn_type)
{
+ struct qwx_dp *dp = &sc->dp;
struct qwx_node *nq = (struct qwx_node *)ni;
struct ath11k_peer *peer;
struct dp_rx_tid *rx_tid;
@@ -24856,19 +24849,26 @@ qwx_peer_rx_tid_setup(struct qwx_softc *sc, struct iee
else
hw_desc_sz = qwx_hal_reo_qdesc_size(DP_BA_WIN_SZ_MAX, tid);
- rx_tid->mem = qwx_dmamem_alloc(sc->sc_dmat, hw_desc_sz,
- HAL_LINK_DESC_ALIGN);
- if (rx_tid->mem == NULL) {
+ if (dp->rx_tid_mem[tid] == NULL) {
+ dp->rx_tid_mem[tid] = qwx_dmamem_alloc(sc->sc_dmat, hw_desc_sz,
+ HAL_LINK_DESC_ALIGN);
+ if (dp->rx_tid_mem[tid] == NULL) {
#ifdef notyet
- spin_unlock_bh(&ab->base_lock);
+ spin_unlock_bh(&ab->base_lock);
#endif
- return ENOMEM;
+ return ENOMEM;
+ }
}
+ rx_tid->mem = dp->rx_tid_mem[tid];
+
vaddr = QWX_DMA_KVA(rx_tid->mem);
qwx_hal_reo_qdesc_setup(vaddr, tid, ba_win_sz, ssn, pn_type);
+ bus_dmamap_sync(sc->sc_dmat, rx_tid->mem->map,
+ 0, rx_tid->mem->size, BUS_DMASYNC_PREREAD);
+
paddr = QWX_DMA_DVA(rx_tid->mem);
rx_tid->vaddr = vaddr;
@@ -26123,10 +26123,6 @@ qwx_auth(struct qwx_softc *sc)
qwx_recalculate_mgmt_rate(sc, ni, arvif->vdev_id, pdev->pdev_id);
ni->ni_txrate = 0;
- ret = qwx_mac_station_add(sc, arvif, pdev->pdev_id, ni);
- if (ret)
- return ret;
-
/* Start vdev. */
ret = qwx_mac_vdev_start(sc, arvif, pdev->pdev_id);
if (ret) {
@@ -26141,17 +26137,31 @@ qwx_auth(struct qwx_softc *sc)
*/
qwx_recalculate_mgmt_rate(sc, ni, arvif->vdev_id, pdev->pdev_id);
- return ret;
+ ret = qwx_mac_station_add(sc, arvif, pdev->pdev_id, ni);
+ if (ret)
+ return ret;
+
+ sc->ops.irq_enable(sc);
+
+ return 0;
}
int
qwx_deauth(struct qwx_softc *sc)
{
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211_node *ni = ic->ic_bss;
+ struct qwx_node *nq = (struct qwx_node *)ni;
struct qwx_vif *arvif = TAILQ_FIRST(&sc->vif_list); /* XXX */
uint8_t pdev_id = 0; /* TODO: derive pdev ID somehow? */
struct ath11k_peer *peer;
int ret;
+ if (ic->ic_opmode == IEEE80211_M_STA) {
+ ic->ic_bss->ni_txrate = 0;
+ nq->flags = 0;
+ }
+
peer = qwx_peer_find_by_id(sc, sc->bss_peer_id);
if (peer == NULL)
return EINVAL;
@@ -26191,7 +26201,7 @@ qwx_peer_assoc_h_basic(struct qwx_softc *sc, struct qw
arg->vdev_id = arvif->vdev_id;
arg->peer_associd = ni->ni_associd;
arg->auth_flag = 1;
- arg->peer_listen_intval = ni->ni_intval;
+ arg->peer_listen_intval = 1;
arg->peer_nss = 1;
arg->peer_caps = ni->ni_capinfo;
}
@@ -26631,13 +26641,6 @@ qwx_assoc(struct qwx_softc *sc)
IEEE80211_ADDR_COPY(arvif->bssid, ni->ni_bssid);
sc->bss_peer_id = nq->peer_id;
- /*
- * Enable reception of data frames now, if not already enabled.
- * We may need to receive EAPOL data frames very soon after the
- * AP sends a response to our assoc request.
- */
- sc->ops.irq_enable(sc);
-
return 0;
}
@@ -26666,12 +26669,14 @@ qwx_run(struct qwx_softc *sc)
DNPRINTF(QWX_D_MAC, "%s: vdev %d up (associated) bssid %s aid %d\n",
__func__, arvif->vdev_id, ether_sprintf(ni->ni_bssid), arvif->aid);
- ret = qwx_wmi_set_peer_param(sc, ni->ni_macaddr, arvif->vdev_id,
- pdev_id, WMI_PEER_AUTHORIZE, 1);
- if (ret) {
- printf("%s: unable to authorize BSS peer: %d\n",
- sc->sc_dev.dv_xname, ret);
- return ret;
+ if ((ic->ic_flags & IEEE80211_F_RSNON) == 0 || ni->ni_port_valid) {
+ ret = qwx_wmi_set_peer_param(sc, ni->ni_macaddr, arvif->vdev_id,
+ pdev_id, WMI_PEER_AUTHORIZE, 1);
+ if (ret) {
+ printf("%s: unable to authorize BSS peer: %d\n",
+ sc->sc_dev.dv_xname, ret);
+ return ret;
+ }
}
return 0;
@@ -26681,12 +26686,19 @@ int
qwx_run_stop(struct qwx_softc *sc)
{
struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = &ic->ic_if;
struct qwx_vif *arvif = TAILQ_FIRST(&sc->vif_list); /* XXX */
uint8_t pdev_id = 0; /* TODO: derive pdev ID somehow? */
struct ieee80211_node *ni = ic->ic_bss;
struct qwx_node *nq = (void *)ni;
int ret;
+ if (ic->ic_opmode == IEEE80211_M_STA &&
+ (ifp->if_flags & IFF_RUNNING) &&
+ (ic->ic_bss->ni_flags & IEEE80211_NODE_MFP) &&
+ ic->ic_bss->ni_port_valid)
+ qwx_mfp_leave(sc);
+
sc->ops.irq_disable(sc);
ret = qwx_wmi_set_peer_param(sc, ni->ni_macaddr, arvif->vdev_id,
blob - 1d0699c9aec0fe9f6ed45a564cadda2a93de6937
blob + 636c2086885099148cf7db56fe361d313d05e517
--- sys/dev/ic/qwxvar.h
+++ sys/dev/ic/qwxvar.h
@@ -1012,7 +1012,7 @@ struct qwx_hp_update_timer {
struct dp_rx_tid {
uint8_t tid;
- struct qwx_dmamem *mem;
+ const struct qwx_dmamem *mem;
uint32_t *vaddr;
uint64_t paddr;
uint32_t size;
@@ -1131,6 +1131,17 @@ struct qwx_dp {
#endif
struct qwx_hp_update_timer reo_cmd_timer;
struct qwx_hp_update_timer tx_ring_timer[DP_TCL_NUM_RING_MAX];
+
+ /*
+ * Cache of DMA memory regions used for Rx aggregation.
+ * We used to free these DMA allocations in interrupt context but
+ * destroying DMA memory in interrupt context is not allowed.
+ *
+ * This array contains enough entries for client station mode.
+ * It will need to grow in order to support multiple clients if
+ * support for HostAP mode gets added to the driver.
+ */
+ struct qwx_dmamem *rx_tid_mem[HAL_DESC_REO_NON_QOS_TID + 1];
};
#define ATH11K_SHADOW_DP_TIMER_INTERVAL 20
blob - e8c86c7809bebb4b78b1ecb1ed39e0d4bd08b8bc
blob + 796d6601a99b2f971aa16d34349ff87f2ce9bff0
--- sys/dev/pci/if_qwx_pci.c
+++ sys/dev/pci/if_qwx_pci.c
@@ -3276,6 +3276,9 @@ qwx_mhi_fw_load_bhi(struct qwx_pci_softc *psc, uint8_t
/* Copy firmware image to DMA memory. */
memcpy(QWX_DMA_KVA(data_adm), data, len);
+ bus_dmamap_sync(sc->sc_dmat, QWX_DMA_MAP(data_adm), 0, len,
+ BUS_DMASYNC_PREWRITE);
+
qwx_pci_write(sc, psc->bhi_off + MHI_BHI_STATUS, 0);
/* Set data physical address and length. */
@@ -3359,6 +3362,11 @@ qwx_mhi_fw_load_bhie(struct qwx_pci_softc *psc, uint8_
/* Copy firmware image to DMA memory. */
memcpy(QWX_DMA_KVA(psc->amss_data), data, len);
+ bus_dmamap_sync(sc->sc_dmat, QWX_DMA_MAP(psc->amss_data), 0, len,
+ BUS_DMASYNC_PREWRITE);
+ bus_dmamap_sync(sc->sc_dmat, QWX_DMA_MAP(psc->amss_vec), 0, vec_size,
+ BUS_DMASYNC_PREWRITE);
+
/* Create vector which controls chunk-wise DMA copy in hardware. */
paddr = QWX_DMA_DVA(psc->amss_data);
vec = QWX_DMA_KVA(psc->amss_vec);