Index | Thread | Search

From:
Kirill A. Korinsky <kirill@korins.ky>
Subject:
Re: The 6th batch of qwz commits
To:
OpenBSD tech <tech@openbsd.org>
Cc:
Stefan Sperling <stsp@stsp.name>, Mark Kettenis <mark.kettenis@xs4all.nl>
Date:
Thu, 01 Oct 2026 14:31:55 +0200

Download raw body.

Thread
On Thu, 01 Oct 2026 13:47:12 +0200,
Mark Kettenis <mark.kettenis@xs4all.nl> wrote:
> 
> > Date: Thu, 1 Oct 2026 13:43:35 +0200
> > From: Stefan Sperling <stsp@stsp.name>
> > 
> > (moved to tech@ from ports@)
> > 
> > On Thu, Oct 01, 2026 at 01:29:40PM +0200, Kirill A. Korinsky wrote:
> > > Stefan,
> > > 
> > > here the 6th batch of qwz commits.
> > > 
> > > It is quite mechanical with exception of one commit where I fix REO layout
> > > to that I had discovered in ath12k, and the next one fixes its lifetime.
> > > 
> > > It was tested on my device and makes it works better, but I may make typos.
> > > 
> > > Ok?
> > 
> > All OK by me, expect the last one of them:
> > 
> > > Subject: [PATCH 7/7] sys/qwz: fix REO queue lifetime
> > > 
> > > Track REO completions before publication and wait for peer unmap,
> > > deletion, and cache flushes before reusing queue DMA. Submission errors
> > > and timeouts retain ownership; hardware failures block reuse until cold
> > > cleanup. HAL error conventions and flush semantics follow ath12k;
> > > tracking and the reuse barrier adapt qwz's retained pool.
> > 
> > This diff is too complicated for me to properly review right now.
> > Since you have tested it and it seems to be a needed improvement to get
> > this driver to work properly, I won't object to it going in without my OK.
> > But I cannot fully endorse this diff.
> > 
> > I don't like the conversion of some "return errno" to "return -errno".
> > The Linux project uses this negative error code convention, but we do not.
> 
> Yeah, please don't do that (outside of drm).
> 


Sorry, here a correct diff. Diff juggling isn't my best skill and that
series is huge, driver is huge and I try to did my best to keep things
clean.

But, In think, I saw -errno a few tiems in that code outside commented code.

Anyway, here the correct diff which I plan to go in in a few hours, after
git mirrors synced with alredy commited into cvs commits, and I prepare
clean export.

commit 222571b1a8c86759327a776273caaa1fe74adea1
Author: Kirill A. Korinsky <kirill@korins.ky>
Date:   Mon Sep 28 01:09:56 2026 +0200

    sys/qwz: fix REO queue lifetime
    
    Track REO completions before publication and wait for peer unmap,
    deletion, and cache flushes before reusing queue DMA. Submission errors
    and timeouts retain ownership; hardware failures block reuse until cold
    cleanup. HAL error conventions and flush semantics follow ath12k;
    tracking and the reuse barrier adapt qwz's retained pool.

diff --git a/sys/dev/ic/qwz.c b/sys/dev/ic/qwz.c
index 56da04766dc..59c4021e3e9 100644
--- a/sys/dev/ic/qwz.c
+++ b/sys/dev/ic/qwz.c
@@ -169,6 +169,7 @@ int qwz_dp_rx_h_null_q_desc(struct qwz_softc *, struct qwz_rx_msdu *,
     struct qwz_rx_msdu_list *);
 void qwz_dp_service_mon_ring(void *);
 void qwz_peer_frags_flush(struct qwz_softc *, struct ath12k_peer *);
+int qwz_peer_rx_tid_cleanup(struct qwz_softc *, struct ath12k_peer *);
 int qwz_wmi_vdev_install_key(struct qwz_softc *,
     struct wmi_vdev_install_key_arg *, uint8_t);
 int qwz_dp_peer_rx_pn_replay_config(struct qwz_softc *, struct qwz_vif *,
@@ -8235,14 +8236,19 @@ qwz_hal_reo_cmd_update_rx_queue(struct hal_tlv_64_hdr *tlv,
 
 int
 qwz_hal_reo_cmd_send(struct qwz_softc *sc, struct hal_srng *srng,
-    enum hal_reo_cmd_type type, struct ath12k_hal_reo_cmd *cmd)
+    enum hal_reo_cmd_type type, struct ath12k_hal_reo_cmd *cmd,
+    struct dp_reo_cmd *dp_cmd)
 {
 	struct hal_tlv_64_hdr *reo_desc;
+	struct dp_reo_cmd *pending;
+	uint32_t hp, reap_hp;
 	int ret;
 #ifdef notyet
 	spin_lock_bh(&srng->lock);
 #endif
 	qwz_hal_srng_access_begin(sc, srng);
+	hp = srng->u.src_ring.hp;
+	reap_hp = srng->u.src_ring.reap_hp;
 	reo_desc = (struct hal_tlv_64_hdr *)
 	    qwz_hal_srng_src_get_next_entry(sc, srng);
 	if (!reo_desc) {
@@ -8274,6 +8280,26 @@ qwz_hal_reo_cmd_send(struct qwz_softc *sc, struct hal_srng *srng,
 		break;
 	}
 
+	if (ret <= 0) {
+		if (ret == 0)
+			ret = EINVAL;
+		goto rollback;
+	}
+	TAILQ_FOREACH(pending, &sc->dp.reo_cmd_list, entry) {
+		if (pending->cmd_num == ret) {
+			ret = EBUSY;
+			goto rollback;
+		}
+	}
+	if (dp_cmd != NULL) {
+		dp_cmd->cmd_num = ret;
+		TAILQ_INSERT_TAIL(&sc->dp.reo_cmd_list, dp_cmd, entry);
+	}
+	goto out;
+
+rollback:
+	srng->u.src_ring.hp = hp;
+	srng->u.src_ring.reap_hp = reap_hp;
 out:
 	qwz_hal_srng_access_end(sc, srng);
 #ifdef notyet
@@ -8994,13 +9020,11 @@ qwz_dp_alloc(struct qwz_softc *sc)
 	dp->sc = sc;
 
 	TAILQ_INIT(&dp->reo_cmd_list);
-	TAILQ_INIT(&dp->reo_cmd_cache_flush_list);
 #if 0
 	INIT_LIST_HEAD(&dp->dp_full_mon_mpdu_list);
 	spin_lock_init(&dp->reo_cmd_lock);
 #endif
 
-	dp->reo_cmd_cache_flush_count = 0;
 	dp->idle_link_rbm = qwz_dp_get_idle_link_rbm(sc);
 
 	ret = qwz_wbm_idle_ring_setup(sc, &n_link_desc);
@@ -9108,7 +9132,6 @@ qwz_dp_reo_cmd_list_cleanup(struct qwz_softc *sc)
 {
 	struct qwz_dp *dp = &sc->dp;
 	struct dp_reo_cmd *cmd, *tmp;
-	struct dp_reo_cache_flush_elem *cmd_cache, *tmp_cache;
 	struct dp_rx_tid *rx_tid;
 #ifdef notyet
 	spin_lock_bh(&dp->reo_cmd_lock);
@@ -9119,15 +9142,6 @@ qwz_dp_reo_cmd_list_cleanup(struct qwz_softc *sc)
 		qwz_dp_rx_tid_clear(sc, rx_tid);
 		free(cmd, M_DEVBUF, sizeof(*cmd));
 	}
-
-	TAILQ_FOREACH_SAFE(cmd_cache, &dp->reo_cmd_cache_flush_list,
-	    entry, tmp_cache) {
-		TAILQ_REMOVE(&dp->reo_cmd_cache_flush_list, cmd_cache, entry);
-		dp->reo_cmd_cache_flush_count--;
-		rx_tid = &cmd_cache->data;
-		qwz_dp_rx_tid_clear(sc, rx_tid);
-		free(cmd_cache, M_DEVBUF, sizeof(*cmd_cache));
-	}
 #ifdef notyet
 	spin_unlock_bh(&dp->reo_cmd_lock);
 #endif
@@ -9169,6 +9183,8 @@ qwz_dp_free(struct qwz_softc *sc)
 		}
 	}
 
+	memset(dp->rx_tid_retire, 0, sizeof(dp->rx_tid_retire));
+
 	/* Deinit any SOC level resource */
 }
 
@@ -16573,7 +16589,6 @@ qwz_dp_process_reo_status(struct qwz_softc *sc)
 	struct hal_reo_status reo_status;
 
 	srng = &sc->hal.srng_list[dp->reo_status_ring.ring_id];
-	memset(&reo_status, 0, sizeof(reo_status));
 #ifdef notyet
 	spin_lock_bh(&srng->lock);
 #endif
@@ -16581,6 +16596,7 @@ qwz_dp_process_reo_status(struct qwz_softc *sc)
 
 	while ((reo_desc = qwz_hal_srng_dst_get_next_entry(sc, srng))) {
 		ret = 1;
+		memset(&reo_status, 0, sizeof(reo_status));
 
 		tlv = (struct hal_tlv_64_hdr *)reo_desc;
 		tag = FIELD_GET(HAL_SRNG_TLV_HDR_TAG, le64toh(tlv->tl));
@@ -16596,6 +16612,10 @@ qwz_dp_process_reo_status(struct qwz_softc *sc)
 		case HAL_REO_FLUSH_CACHE_STATUS:
 			qwz_hal_reo_flush_cache_status(sc, reo_desc,
 			    &reo_status);
+			if (reo_status.u.flush_cache.err_detected ||
+			    reo_status.u.flush_cache.err_code ||
+			    reo_status.u.flush_cache.cache_controller_flush_status_err)
+				reo_status.uniform_hdr.cmd_status = HAL_REO_CMD_FAILED;
 			break;
 		case HAL_REO_UNBLOCK_CACHE_STATUS:
 			qwz_hal_reo_unblk_cache_status(sc, reo_desc,
@@ -16632,8 +16652,9 @@ qwz_dp_process_reo_status(struct qwz_softc *sc)
 		spin_unlock_bh(&dp->reo_cmd_lock);
 #endif
 		if (found) {
-			cmd->handler(dp, (void *)&cmd->data,
-			    reo_status.uniform_hdr.cmd_status);
+			if (cmd->handler != NULL)
+				cmd->handler(dp, (void *)&cmd->data,
+				    reo_status.uniform_hdr.cmd_status);
 			free(cmd, M_DEVBUF, sizeof(*cmd));
 		}
 		found = 0;
@@ -22609,6 +22630,13 @@ qwz_peer_delete(struct qwz_softc *sc, uint32_t vdev_id, uint8_t pdev_id,
 		}
 	}
 
+	ret = qwz_peer_rx_tid_cleanup(sc, peer);
+	if (ret) {
+		printf("%s: failed to retire peer RX queues: %d\n",
+		    sc->sc_dev.dv_xname, ret);
+		return ret;
+	}
+
 	TAILQ_REMOVE(&sc->peers, peer, entry);
 	qwz_node_clear_peer_id(sc, peer);
 	free(peer, M_DEVBUF, sizeof(*peer));
@@ -22673,44 +22701,27 @@ qwz_dp_tx_send_reo_cmd(struct qwz_softc *sc, struct dp_rx_tid *rx_tid,
     void (*cb)(struct qwz_dp *, void *, enum hal_reo_cmd_status))
 {
 	struct qwz_dp *dp = &sc->dp;
-	struct dp_reo_cmd *dp_cmd;
+	struct dp_reo_cmd *dp_cmd = NULL;
 	struct hal_srng *cmd_ring;
 	int cmd_num;
 
 	if (test_bit(ATH12K_FLAG_CRASH_FLUSH, sc->sc_flags))
 		return ESHUTDOWN;
 
+	if (cmd->flag & HAL_REO_CMD_FLG_NEED_STATUS) {
+		dp_cmd = malloc(sizeof(*dp_cmd), M_DEVBUF, M_ZERO | M_NOWAIT);
+		if (dp_cmd == NULL)
+			return ENOMEM;
+		memcpy(&dp_cmd->data, rx_tid, sizeof(*rx_tid));
+		dp_cmd->handler = cb;
+	}
+
 	cmd_ring = &sc->hal.srng_list[dp->reo_cmd_ring.ring_id];
-	cmd_num = qwz_hal_reo_cmd_send(sc, cmd_ring, type, cmd);
-	/* cmd_num should start from 1, during failure return the error code */
-	if (cmd_num < 0)
+	cmd_num = qwz_hal_reo_cmd_send(sc, cmd_ring, type, cmd, dp_cmd);
+	if (cmd_num < 0) {
+		free(dp_cmd, M_DEVBUF, sizeof(*dp_cmd));
 		return cmd_num;
-
-	/* reo cmd ring descriptors has cmd_num starting from 1 */
-	if (cmd_num == 0)
-		return EINVAL;
-
-	if (!cb)
-		return 0;
-
-	/* Can this be optimized so that we keep the pending command list only
-	 * for tid delete command to free up the resource on the command status
-	 * indication?
-	 */
-	dp_cmd = malloc(sizeof(*dp_cmd), M_DEVBUF, M_ZERO | M_NOWAIT);
-	if (!dp_cmd)
-		return ENOMEM;
-
-	memcpy(&dp_cmd->data, rx_tid, sizeof(struct dp_rx_tid));
-	dp_cmd->cmd_num = cmd_num;
-	dp_cmd->handler = cb;
-#ifdef notyet
-	spin_lock_bh(&dp->reo_cmd_lock);
-#endif
-	TAILQ_INSERT_TAIL(&dp->reo_cmd_list, dp_cmd, entry);
-#ifdef notyet
-	spin_unlock_bh(&dp->reo_cmd_lock);
-#endif
+	}
 	return 0;
 }
 
@@ -22825,141 +22836,131 @@ qwz_hal_reo_qdesc_setup(void *vaddr, int tid, uint32_t ba_window_size,
 }
 
 void
-qwz_dp_reo_cmd_free(struct qwz_dp *dp, void *ctx,
+qwz_dp_rx_tid_retire_done(struct qwz_dp *dp, void *ctx,
     enum hal_reo_cmd_status status)
 {
-	struct qwz_softc *sc = dp->sc;
 	struct dp_rx_tid *rx_tid = ctx;
-
-	if (status != HAL_REO_CMD_SUCCESS)
-		printf("%s: failed to flush rx tid hw desc, tid %d status %d\n",
-		    sc->sc_dev.dv_xname, rx_tid->tid, status);
-
-	qwz_dp_rx_tid_clear(sc, rx_tid);
+	struct qwz_rx_tid_retire *retire = &dp->rx_tid_retire[rx_tid->tid];
+
+	KASSERT(retire->data.mem == rx_tid->mem);
+	KASSERT(retire->state == QWZ_RX_TID_DELETE_PENDING ||
+	    retire->state == QWZ_RX_TID_FLUSH_PENDING);
+
+	if (status != HAL_REO_CMD_SUCCESS) {
+		retire->state = QWZ_RX_TID_RETIRE_FAILED;
+		printf("%s: RX queue retirement failed, tid %u status %d\n",
+		    dp->sc->sc_dev.dv_xname, rx_tid->tid, status);
+	} else if (retire->state == QWZ_RX_TID_DELETE_PENDING) {
+		retire->flush_offset = retire->data.size;
+		retire->state = QWZ_RX_TID_FLUSH_READY;
+	} else if (retire->flush_offset != 0) {
+		retire->state = QWZ_RX_TID_FLUSH_READY;
+	} else {
+		memset(retire, 0, sizeof(*retire));
+	}
+	wakeup(dp->rx_tid_retire);
 }
 
-void
-qwz_dp_reo_cache_flush(struct qwz_softc *sc, struct dp_rx_tid *rx_tid)
+int
+qwz_dp_rx_tid_retire_submit(struct qwz_softc *sc,
+    struct qwz_rx_tid_retire *retire)
 {
 	struct ath12k_hal_reo_cmd cmd = {0};
-	unsigned long tot_desc_sz, desc_sz;
+	enum qwz_rx_tid_retire_state state = retire->state;
+	enum hal_reo_cmd_type type;
+	uint32_t offset = retire->flush_offset;
+	uint64_t paddr = retire->data.paddr;
 	int ret;
 
-	tot_desc_sz = rx_tid->size;
-	desc_sz = qwz_hal_reo_qdesc_size(0, HAL_DESC_REO_NON_QOS_TID);
-
-	while (tot_desc_sz > desc_sz) {
-		tot_desc_sz -= desc_sz;
-		cmd.addr_lo = (rx_tid->paddr + tot_desc_sz) & 0xffffffff;
-		cmd.addr_hi = rx_tid->paddr >> 32;
-		ret = qwz_dp_tx_send_reo_cmd(sc, rx_tid,
-		    HAL_REO_CMD_FLUSH_CACHE, &cmd, NULL);
-		if (ret) {
-			printf("%s: failed to send HAL_REO_CMD_FLUSH_CACHE, "
-			    "tid %d (%d)\n", sc->sc_dev.dv_xname, rx_tid->tid,
-			    ret);
-		}
-	}
-
-	memset(&cmd, 0, sizeof(cmd));
-	cmd.addr_lo = rx_tid->paddr & 0xffffffff;
-	cmd.addr_hi = rx_tid->paddr >> 32;
-	cmd.flag |= HAL_REO_CMD_FLG_NEED_STATUS;
-	ret = qwz_dp_tx_send_reo_cmd(sc, rx_tid, HAL_REO_CMD_FLUSH_CACHE,
-	    &cmd, qwz_dp_reo_cmd_free);
+	cmd.flag = HAL_REO_CMD_FLG_NEED_STATUS;
+	if (state == QWZ_RX_TID_DELETE_READY) {
+		/* WCN7850 requires VLD to remain set until the cache flush. */
+		cmd.upd0 = HAL_REO_CMD_UPD0_VLD;
+		cmd.upd1 = HAL_REO_CMD_UPD1_VLD;
+		type = HAL_REO_CMD_UPDATE_RX_QUEUE;
+		retire->state = QWZ_RX_TID_DELETE_PENDING;
+	} else {
+		KASSERT(state == QWZ_RX_TID_FLUSH_READY);
+		KASSERT(offset >= sizeof(struct hal_rx_reo_queue));
+		retire->flush_offset -= sizeof(struct hal_rx_reo_queue);
+		paddr += retire->flush_offset;
+		if (retire->flush_offset == 0)
+			cmd.flag |= HAL_REO_CMD_FLG_FLUSH_FWD_ALL_MPDUS;
+		type = HAL_REO_CMD_FLUSH_CACHE;
+		retire->state = QWZ_RX_TID_FLUSH_PENDING;
+	}
+	cmd.addr_lo = paddr & 0xffffffff;
+	cmd.addr_hi = paddr >> 32;
+	ret = qwz_dp_tx_send_reo_cmd(sc, &retire->data, type, &cmd,
+	    qwz_dp_rx_tid_retire_done);
 	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);
-		qwz_dp_rx_tid_clear(sc, rx_tid);
+		retire->state = state;
+		retire->flush_offset = offset;
 	}
+	return ret;
 }
 
-void
-qwz_dp_rx_tid_del_func(struct qwz_dp *dp, void *ctx,
-    enum hal_reo_cmd_status status)
+int
+qwz_dp_rx_tid_retire_wait(struct qwz_softc *sc)
 {
-	struct qwz_softc *sc = dp->sc;
-	struct dp_rx_tid *rx_tid = ctx;
-	struct dp_reo_cache_flush_elem *elem, *tmp;
+	struct qwz_dp *dp = &sc->dp;
+	struct qwz_rx_tid_retire *retire;
+	uint64_t deadline = getnsecuptime() + SEC_TO_NSEC(3);
 	uint64_t now;
+	int i, pending, error, ret;
 
-	if (status == HAL_REO_CMD_DRAIN) {
-		qwz_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",
-		    sc->sc_dev.dv_xname, rx_tid->tid, status);
-		return;
-	}
-
-	elem = malloc(sizeof(*elem), M_DEVBUF, M_ZERO | M_NOWAIT);
-	if (!elem) {
-		qwz_dp_rx_tid_clear(sc, rx_tid);
-		return;
-	}
-
-	now = getnsecuptime();
-	elem->ts = now;
-	memcpy(&elem->data, rx_tid, sizeof(*rx_tid));
-
-	qwz_dp_rx_tid_clear(sc, rx_tid);
-
-#ifdef notyet
-	spin_lock_bh(&dp->reo_cmd_lock);
-#endif
-	TAILQ_INSERT_TAIL(&dp->reo_cmd_cache_flush_list, elem, entry);
-	dp->reo_cmd_cache_flush_count++;
-
-	/* Flush and invalidate aged REO desc from HW cache */
-	TAILQ_FOREACH_SAFE(elem, &dp->reo_cmd_cache_flush_list, entry, tmp) {
-		if (dp->reo_cmd_cache_flush_count > DP_REO_DESC_FREE_THRESHOLD ||
-		    now >= elem->ts + MSEC_TO_NSEC(DP_REO_DESC_FREE_TIMEOUT_MS)) {
-			TAILQ_REMOVE(&dp->reo_cmd_cache_flush_list, elem, entry);
-			dp->reo_cmd_cache_flush_count--;
-#ifdef notyet
-			spin_unlock_bh(&dp->reo_cmd_lock);
-#endif
-			qwz_dp_reo_cache_flush(sc, &elem->data);
-			free(elem, M_DEVBUF, sizeof(*elem));
-#ifdef notyet
-			spin_lock_bh(&dp->reo_cmd_lock);
-#endif
+	for (;;) {
+		pending = 0;
+		error = 0;
+		for (i = 0; i < nitems(dp->rx_tid_retire); i++) {
+			retire = &dp->rx_tid_retire[i];
+			switch (retire->state) {
+			case QWZ_RX_TID_REUSABLE:
+				break;
+			case QWZ_RX_TID_DELETE_READY:
+			case QWZ_RX_TID_FLUSH_READY:
+				ret = qwz_dp_rx_tid_retire_submit(sc, retire);
+				if (ret && !error)
+					error = ret;
+				pending = 1;
+				break;
+			case QWZ_RX_TID_DELETE_PENDING:
+			case QWZ_RX_TID_FLUSH_PENDING:
+				pending = 1;
+				break;
+			case QWZ_RX_TID_RETIRE_FAILED:
+				error = EIO;
+				break;
+			}
 		}
+		if (error)
+			return error;
+		if (!pending)
+			return 0;
+		now = getnsecuptime();
+		if (now >= deadline)
+			return ETIMEDOUT;
+		ret = tsleep_nsec(dp->rx_tid_retire, 0, "qwzreo",
+		    deadline - now);
+		if (ret)
+			return ret;
 	}
-#ifdef notyet
-	spin_unlock_bh(&dp->reo_cmd_lock);
-#endif
 }
 
 void
 qwz_peer_rx_tid_delete(struct qwz_softc *sc, struct ath12k_peer *peer,
     uint8_t tid)
 {
-	struct ath12k_hal_reo_cmd cmd = {0};
 	struct dp_rx_tid *rx_tid = &peer->rx_tid[tid];
-	int ret;
+	struct qwz_rx_tid_retire *retire = &sc->dp.rx_tid_retire[tid];
 
 	if (!rx_tid->active)
 		return;
 
+	KASSERT(retire->state == QWZ_RX_TID_REUSABLE);
+	retire->data = *rx_tid;
+	retire->state = QWZ_RX_TID_DELETE_READY;
 	rx_tid->active = 0;
-
-	cmd.flag = HAL_REO_CMD_FLG_NEED_STATUS;
-	cmd.addr_lo = rx_tid->paddr & 0xffffffff;
-	cmd.addr_hi = rx_tid->paddr >> 32;
-	cmd.upd0 |= HAL_REO_CMD_UPD0_VLD;
-	ret = qwz_dp_tx_send_reo_cmd(sc, rx_tid, HAL_REO_CMD_UPDATE_RX_QUEUE,
-	    &cmd, qwz_dp_rx_tid_del_func);
-	if (ret) {
-		if (ret != ESHUTDOWN) {
-			printf("%s: failed to send "
-			    "HAL_REO_CMD_UPDATE_RX_QUEUE cmd, tid %d (%d)\n",
-			    sc->sc_dev.dv_xname, tid, ret);
-		}
-
-		qwz_dp_rx_tid_clear(sc, rx_tid);
-	}
 }
 
 void
@@ -23006,11 +23007,11 @@ qwz_peer_frags_flush(struct qwz_softc *sc, struct ath12k_peer *peer)
 	}
 }
 
-void
+int
 qwz_peer_rx_tid_cleanup(struct qwz_softc *sc, struct ath12k_peer *peer)
 {
 	struct dp_rx_tid *rx_tid;
-	int i;
+	int i, ret;
 #ifdef notyet
 	lockdep_assert_held(&ar->ab->base_lock);
 #endif
@@ -23025,6 +23026,12 @@ qwz_peer_rx_tid_cleanup(struct qwz_softc *sc, struct ath12k_peer *peer)
 		spin_lock_bh(&ar->ab->base_lock);
 #endif
 	}
+	ret = qwz_dp_rx_tid_retire_wait(sc);
+	if (ret)
+		return ret;
+	for (i = 0; i <= HAL_DESC_REO_NON_QOS_TID; i++)
+		qwz_dp_rx_tid_clear(sc, &peer->rx_tid[i]);
+	return 0;
 }
 
 int
@@ -23105,6 +23112,9 @@ qwz_peer_rx_tid_setup(struct qwz_softc *sc, struct ieee80211_node *ni,
 	peer = qwz_peer_find_by_id(sc, nq->peer_id);
 	if (peer == NULL)
 		return ENOENT;
+	if (peer->delete_pending || peer->unmapped ||
+	    dp->rx_tid_retire[tid].state != QWZ_RX_TID_REUSABLE)
+		return EBUSY;
 
 #ifdef notyet
 	spin_lock_bh(&ab->base_lock);
@@ -23712,8 +23722,6 @@ qwz_mac_station_remove(struct qwz_softc *sc, struct qwz_vif *arvif,
 {
 	int ret;
 
-	qwz_peer_rx_tid_cleanup(sc, peer);
-
 	ret = qwz_peer_delete(sc, arvif->vdev_id, pdev_id, peer);
 	if (ret) {
 		printf("%s: unable to delete BSS peer: %d\n",
diff --git a/sys/dev/ic/qwzvar.h b/sys/dev/ic/qwzvar.h
index d20b1a4b4f4..3f9bae208e1 100644
--- a/sys/dev/ic/qwzvar.h
+++ b/sys/dev/ic/qwzvar.h
@@ -1146,18 +1146,23 @@ struct dp_rx_tid {
 #endif
 };
 
-#define DP_REO_DESC_FREE_THRESHOLD  64
-#define DP_REO_DESC_FREE_TIMEOUT_MS 1000
 #define DP_MON_PURGE_TIMEOUT_MS     100
 #define DP_MON_SERVICE_BUDGET       128
 
-struct dp_reo_cache_flush_elem {
-	TAILQ_ENTRY(dp_reo_cache_flush_elem) entry;
-	struct dp_rx_tid data;
-	uint64_t ts;
+enum qwz_rx_tid_retire_state {
+	QWZ_RX_TID_REUSABLE,
+	QWZ_RX_TID_DELETE_READY,
+	QWZ_RX_TID_DELETE_PENDING,
+	QWZ_RX_TID_FLUSH_READY,
+	QWZ_RX_TID_FLUSH_PENDING,
+	QWZ_RX_TID_RETIRE_FAILED,
 };
 
-TAILQ_HEAD(dp_reo_cmd_cache_flush_head, dp_reo_cache_flush_elem);
+struct qwz_rx_tid_retire {
+	struct dp_rx_tid data;
+	enum qwz_rx_tid_retire_state state;
+	uint32_t flush_offset;
+};
 
 struct dp_reo_cmd {
 	TAILQ_ENTRY(dp_reo_cmd) entry;
@@ -1257,18 +1262,14 @@ struct qwz_dp {
 	struct dp_tx_ring tx_ring[DP_TCL_NUM_RING_MAX];
 	struct hal_wbm_idle_scatter_list scatter_list[DP_IDLE_SCATTER_BUFS_MAX];
 	struct dp_reo_cmd_head reo_cmd_list;
-	struct dp_reo_cmd_cache_flush_head reo_cmd_cache_flush_list;
 #if 0
 	struct list_head dp_full_mon_mpdu_list;
 #endif
-	uint32_t reo_cmd_cache_flush_count;
 	enum hal_rx_buf_return_buf_manager idle_link_rbm;
 #if 0
 	/**
 	 * protects access to below fields,
 	 * - reo_cmd_list
-	 * - reo_cmd_cache_flush_list
-	 * - reo_cmd_cache_flush_count
 	 */
 	spinlock_t reo_cmd_lock;
 #endif
@@ -1302,6 +1303,7 @@ struct qwz_dp {
 	 * support for HostAP mode gets added to the driver.
 	 */
 	struct qwz_dmamem *rx_tid_mem[HAL_DESC_REO_NON_QOS_TID + 1];
+	struct qwz_rx_tid_retire rx_tid_retire[HAL_DESC_REO_NON_QOS_TID + 1];
 };
 
 #define ATH12K_SHADOW_DP_TIMER_INTERVAL 20


-- 
wbr, Kirill