Re: small set of qwx bug fixes
Peter Hessler <[email protected]>
| Newsgroups | gmane.os.openbsd.tech |
|---|---|
| Message-ID | <[email protected]> |
On 2026 Jun 23 (Tue) at 11:16:31 +0200 (+0200), Stefan Sperling wrote:
:On Mon, Jun 22, 2026 at 01:43:57PM -0700, Greg Steuck wrote:
:> resume in -current to ~reliable. I got bored after 16 wakeups and the
:> running ping recovering with no manual intervention. Ship it!
:
:There is a regression with one of the changes. I've seen mcl2k pool
:corruption checks trigger again after about a day of running the previous
:diff. I worked hard on preventing pool curruption some months ago, and
:seeing it return is annoying.
:
:I might have found the reason: The previous diff skipped calling
:qwx_flush_rx_rings() in some cases. This function is our crude way of
:preventing firmware from doing more Rx, by making all Rx rings empty.
:If we don't call this function then firmware might still be doing DMA
:on a ring we have freed, since the ring pointers it has cached imply
:non-empty rings.
:
:Please test this version, which always keeps calling qwx_flush_rx_rings()
:unless loading firmware failed. These panics don't trigger very easily.
:Having several users of the 2k mbuf cluster pool helps to trigger them,
:e.g. when using ethernet and wifi in parallel.
:
:If no crashes occur within a few days of use, this can be considered solid.
:
been running with this on my X13s for a couple days, all solid.
OK
:M sys/dev/ic/qwx.c | 67+ 52-
:M sys/dev/pci/if_qwx_pci.c | 2+ 1-
:
:2 files changed, 69 insertions(+), 53 deletions(-)
:
:commit - 06943c8222484abb6aad11c1b1d5f752922ba0d1
:commit + bb95bd9ab9390805516cf7407ab355934bcfa93d
:blob - 4c744b205a09dec658faed98adb3e4e7a7f557be
:blob + 77c7504ab632b53f0cfc75d09e8d2a4038c990a5
:--- sys/dev/ic/qwx.c
:+++ sys/dev/ic/qwx.c
:@@ -271,13 +271,16 @@ qwx_init(struct ifnet *ifp)
: sc->scan.state = ATH11K_SCAN_IDLE;
: sc->vdev_id_11d_scan = QWX_11D_INVALID_VDEV_ID;
:
:- error = qwx_core_init(sc);
:- if (error)
:- return error;
:-
: memset(&sc->qrtr_server, 0, sizeof(sc->qrtr_server));
: sc->qrtr_server.node = QRTR_NODE_BCAST;
:
:+ /* This flag will be cleared if firmware starts up successfully. */
:+ set_bit(ATH11K_FLAG_QMI_FAIL, sc->sc_flags);
:+
:+ error = qwx_core_init(sc);
:+ if (error)
:+ return error;
:+
: /* wait for QRTR init to be done */
: while (sc->qrtr_server.node == QRTR_NODE_BCAST) {
: error = tsleep_nsec(&sc->qrtr_server, 0, "qwxqrtr",
:@@ -309,19 +312,18 @@ qwx_init(struct ifnet *ifp)
: sc->sc_dev.dv_xname, ether_sprintf(ic->ic_myaddr),
: error);
:
:- ieee80211_media_init(ifp, qwx_media_change,
:+ ieee80211_media_init(ifp, ieee80211_media_change,
: ieee80211_media_status);
: }
:
: if (ifp->if_flags & IFF_UP) {
:- refcnt_init(&sc->task_refs);
:-
:- ifq_clr_oactive(&ifp->if_snd);
:-
: error = qwx_mac_start(sc);
: if (error)
: return error;
:
:+ refcnt_init(&sc->task_refs);
:+ ifq_clr_oactive(&ifp->if_snd);
:+
: ifp->if_flags |= IFF_RUNNING;
: sc->ops.irq_enable(sc);
: ieee80211_begin_scan(ifp);
:@@ -414,10 +416,12 @@ qwx_stop(struct ifnet *ifp)
: struct qwx_softc *sc = ifp->if_softc;
: struct ieee80211com *ic = &sc->sc_ic;
: int s = splnet();
:+ int was_running;
:
: rw_assert_wrlock(&sc->ioctl_rwl);
:
:- if (ic->ic_opmode == IEEE80211_M_STA &&
:+ was_running = (ifp->if_flags & IFF_RUNNING) != 0;
:+ if (was_running && 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)
:@@ -433,7 +437,8 @@ qwx_stop(struct ifnet *ifp)
: /* Cancel scheduled tasks and let any stale tasks finish up. */
: task_del(systq, &sc->init_task);
: qwx_del_task_all(sc);
:- refcnt_finalize(&sc->task_refs, "qwxstop");
:+ if (was_running)
:+ refcnt_finalize(&sc->task_refs, "qwxstop");
:
: ifp->if_timer = sc->sc_tx_timer = 0;
:
:@@ -444,20 +449,21 @@ qwx_stop(struct ifnet *ifp)
: sc->bgscan_unref_arg = NULL;
: sc->bgscan_unref_arg_size = 0;
:
:- 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
: * from our current AP, and/or stop the VIF, etc.
: */
:- if (ic->ic_state != IEEE80211_S_INIT) {
:+ if (was_running && ic->ic_state != IEEE80211_S_INIT) {
: sc->ns_nstate = IEEE80211_S_INIT;
: sc->ns_arg = -1; /* do not send management frames */
: refcnt_init(&sc->task_refs);
: refcnt_take(&sc->task_refs);
:+ clear_bit(ATH11K_FLAG_CRASH_FLUSH, sc->sc_flags);
: qwx_newstate_task(sc);
:- if (ic->ic_state != IEEE80211_S_INIT) { /* task code failed */
:+ set_bit(ATH11K_FLAG_CRASH_FLUSH, sc->sc_flags);
:+ if (ic->ic_state != IEEE80211_S_INIT) {
:+ /* task code failed */
: task_del(systq, &sc->init_task);
: sc->sc_newstate(ic, IEEE80211_S_INIT, -1);
: }
:@@ -472,7 +478,14 @@ qwx_stop(struct ifnet *ifp)
: sc->vdev_id_11d_scan = QWX_11D_INVALID_VDEV_ID;
: sc->pdevs_active = 0;
:
:- /* power off hardware */
:+ /*
:+ * If we were running then allow commands to be sent to
:+ * firmware during core_deinit().
:+ */
:+ if (was_running)
:+ clear_bit(ATH11K_FLAG_CRASH_FLUSH, sc->sc_flags);
:+
:+ /* free some DMA allocations and power off hardware */
: qwx_core_deinit(sc);
:
: qwx_vif_free_all(sc);
:@@ -517,6 +530,10 @@ qwx_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
: /* Force reload of firmware image from disk. */
: qwx_free_firmware(sc);
: err = qwx_init(ifp);
:+ if (err) {
:+ KASSERT(!(ifp->if_flags & IFF_RUNNING));
:+ qwx_stop(ifp);
:+ }
: }
: } else {
: if (ifp->if_flags & IFF_RUNNING)
:@@ -681,24 +698,6 @@ qwx_watchdog(struct ifnet *ifp)
: }
:
: int
:-qwx_media_change(struct ifnet *ifp)
:-{
:- int err;
:-
:- err = ieee80211_media_change(ifp);
:- if (err != ENETRESET)
:- return err;
:-
:- if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) ==
:- (IFF_UP | IFF_RUNNING)) {
:- qwx_stop(ifp);
:- err = qwx_init(ifp);
:- }
:-
:- return err;
:-}
:-
:-int
: qwx_queue_setkey_cmd(struct ieee80211com *ic, struct ieee80211_node *ni,
: struct ieee80211_key *k, int cmd)
: {
:@@ -15353,6 +15352,9 @@ qwx_dp_rxdma_ring_buf_setup(struct qwx_softc *sc,
: if (rx_ring->rx_data == NULL)
: return ENOMEM;
:
:+ rx_ring->bufs_max = num_entries;
:+ memset(rx_ring->freemap, 0xff, sizeof(rx_ring->freemap));
:+
: for (i = 0; i < num_entries; i++) {
: struct qwx_rx_data *rx_data = &rx_ring->rx_data[i];
:
:@@ -15361,9 +15363,6 @@ qwx_dp_rxdma_ring_buf_setup(struct qwx_softc *sc,
: return ENOMEM;
: }
:
:- rx_ring->bufs_max = num_entries;
:- memset(rx_ring->freemap, 0xff, sizeof(rx_ring->freemap));
:-
: return qwx_dp_rxbufs_replenish(sc, dp->mac_id, rx_ring, num_entries,
: sc->hw_params.hal_params->rx_buf_rbm);
: }
:@@ -20786,11 +20785,13 @@ qwx_flush_rx_rings(struct qwx_softc *sc)
: void
: qwx_core_stop(struct qwx_softc *sc)
: {
:- if (!test_bit(ATH11K_FLAG_CRASH_FLUSH, sc->sc_flags))
:+ if (!test_bit(ATH11K_FLAG_CRASH_FLUSH, sc->sc_flags) &&
:+ !test_bit(ATH11K_FLAG_QMI_FAIL, sc->sc_flags))
: qwx_qmi_firmware_stop(sc);
:-
:- qwx_flush_rx_rings(sc);
:
:+ if (!test_bit(ATH11K_FLAG_QMI_FAIL, sc->sc_flags))
:+ qwx_flush_rx_rings(sc);
:+
: sc->ops.stop(sc);
: qwx_wmi_detach(sc);
: qwx_dp_pdev_reo_cleanup(sc);
:@@ -20921,7 +20922,8 @@ qwx_core_qmi_firmware_ready(struct qwx_softc *sc)
: default:
: printf("%s: invalid crypto_mode: %d\n",
: sc->sc_dev.dv_xname, sc->crypto_mode);
:- return EINVAL;
:+ ret = EINVAL;
:+ goto err_dp_free;
: }
:
: if (sc->frame_mode == ATH11K_HW_TXRX_RAW)
:@@ -20970,7 +20972,7 @@ err_firmware_stop:
: return ret;
: }
:
:-void
:+int
: qwx_qmi_fw_init_done(struct qwx_softc *sc)
: {
: int ret = 0;
:@@ -20984,10 +20986,15 @@ qwx_qmi_fw_init_done(struct qwx_softc *sc)
: clear_bit(ATH11K_FLAG_RECOVERY, sc->sc_flags);
: ret = qwx_core_qmi_firmware_ready(sc);
: if (ret) {
:+ /*
:+ * This flags tells qwx_stop() that core is stopped
:+ * and ATH11K_FIRMWARE_MODE_OFF was already sent.
:+ */
: set_bit(ATH11K_FLAG_QMI_FAIL, sc->sc_flags);
:- return;
: }
: }
:+
:+ return ret;
: }
:
: int
:@@ -21047,8 +21054,7 @@ qwx_qmi_event_server_arrive(struct qwx_softc *sc)
: }
: }
:
:- qwx_qmi_fw_init_done(sc);
:- return 0;
:+ return qwx_qmi_fw_init_done(sc);
: }
:
: int
:@@ -21577,11 +21583,8 @@ qwx_hal_free_cont_wrp(struct qwx_softc *sc)
: int
: qwx_hal_srng_init(struct qwx_softc *sc)
: {
:- struct ath11k_hal *hal = &sc->hal;
: int ret;
:
:- memset(hal, 0, sizeof(*hal));
:-
: ret = qwx_hal_srng_create_config(sc);
: if (ret)
: goto err_hal;
:@@ -22661,7 +22664,8 @@ qwx_ce_cleanup_pipes(struct qwx_softc *sc)
: qwx_ce_rx_pipe_cleanup(pipe);
:
: /* Cleanup any src CE's which have interrupts disabled */
:- qwx_ce_poll_send_completed(sc, pipe_num);
:+ if (!test_bit(ATH11K_FLAG_QMI_FAIL, sc->sc_flags))
:+ qwx_ce_poll_send_completed(sc, pipe_num);
: }
: }
:
:@@ -24323,8 +24327,17 @@ qwx_init_task(void *arg)
: struct qwx_softc *sc = arg;
: struct ifnet *ifp = &sc->sc_ic.ic_if;
: int s = splnet();
:- rw_enter_write(&sc->ioctl_rwl);
:
:+ /*
:+ * Do not sleep for this lock. The init task is a one-shot
:+ * recovery mechanism. If the ioctl handler is busy then
:+ * we are being reconfigured or reset already.
:+ */
:+ if (rw_enter(&sc->ioctl_rwl, RW_WRITE | RW_NOSLEEP) != 0) {
:+ splx(s);
:+ return;
:+ }
:+
: if (ifp->if_flags & IFF_RUNNING)
: qwx_stop(ifp);
:
:@@ -27444,11 +27457,11 @@ qwx_activate(struct device *self, int act)
:
: switch (act) {
: case DVACT_QUIESCE:
:+ rw_enter_write(&sc->ioctl_rwl);
: if (ifp->if_flags & IFF_RUNNING) {
:- rw_enter_write(&sc->ioctl_rwl);
: qwx_stop(ifp);
:- rw_exit(&sc->ioctl_rwl);
: }
:+ rw_exit(&sc->ioctl_rwl);
: break;
: case DVACT_RESUME:
: err = qwx_hal_srng_init(sc);
:@@ -27457,12 +27470,14 @@ qwx_activate(struct device *self, int act)
: sc->sc_dev.dv_xname);
: break;
: case DVACT_WAKEUP:
:+ rw_enter_write(&sc->ioctl_rwl);
: if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) == IFF_UP) {
: err = qwx_init(ifp);
: if (err)
: printf("%s: could not initialize hardware\n",
: sc->sc_dev.dv_xname);
: }
:+ rw_exit(&sc->ioctl_rwl);
: break;
: }
:
:blob - a0e416a3cafea6c49646852c11c9fa9045b71ce2
:blob + 00da7a38928ecf5dee26f3b73a8eeb10ca0fd04c
:--- sys/dev/pci/if_qwx_pci.c
:+++ sys/dev/pci/if_qwx_pci.c
:@@ -1113,7 +1113,8 @@ unsupported_wcn6855_soc:
: memcpy(ifp->if_xname, sc->sc_dev.dv_xname, IFNAMSIZ);
: if_attach(ifp);
: ieee80211_ifattach(ifp);
:- ieee80211_media_init(ifp, qwx_media_change, ieee80211_media_status);
:+ ieee80211_media_init(ifp, ieee80211_media_change,
:+ ieee80211_media_status);
:
: ic->ic_node_alloc = qwx_node_alloc;
:
:
:
:
:
--
Bizoos, n.:
The millions of tiny individual bumps that make up a
basketball.
-- Rich Hall, "Sniglets"