[PATCH ath-next v3 08/13] wifi: ath12k: ng: add batched REO command sends
From: Pradeep Kumar Chitrapu
Date: Tue Nov 03 2026 - 20:14:19 EST
RX TID setup needs to send related REO operations together so hardware
sees a consistent queue state and the driver can keep one pending-command
view for the whole operation.
Add Wi-Fi 8 helpers to submit multiple REO commands to one command ring
while reusing the existing pending-command callback tracking. Share the
HAL descriptor formatter between the batch and single-command paths.
Reject batched flush-cache commands that request delayed blocking
because those commands mutate the shared REO block-resource state. If
descriptor formatting fails after reserving entries, restore the source
ring pointers before exposing stale descriptors to hardware; keep the
same rollback behavior for the single-command path.
Tested-on: Compilation only, M=drivers/net/wireless/ath/ath12k
Signed-off-by: Pradeep Kumar Chitrapu <pradeep.chitrapu@xxxxxxxxxxxxxxxx>
---
drivers/net/wireless/ath/ath12k/hal.h | 6 +
drivers/net/wireless/ath/ath12k/wifi8/dp_rx.c | 184 ++++++++++++++++++++++---
drivers/net/wireless/ath/ath12k/wifi8/dp_rx.h | 12 ++
drivers/net/wireless/ath/ath12k/wifi8/hal_rx.c | 138 +++++++++++++++----
drivers/net/wireless/ath/ath12k/wifi8/hal_rx.h | 9 ++
5 files changed, 297 insertions(+), 52 deletions(-)
diff --git a/drivers/net/wireless/ath/ath12k/hal.h b/drivers/net/wireless/ath/ath12k/hal.h
index 6534e11a3378..b4da48af5a4b 100644
--- a/drivers/net/wireless/ath/ath12k/hal.h
+++ b/drivers/net/wireless/ath/ath12k/hal.h
@@ -1141,6 +1141,12 @@ struct ath12k_hal_reo_cmd {
u8 pn_127_48_info;
};
+struct ath12k_reo_cmd_entry {
+ enum hal_reo_cmd_type type;
+ struct ath12k_hal_reo_cmd cmd;
+ int cmd_num;
+};
+
enum hal_pn_type {
HAL_PN_TYPE_NONE,
HAL_PN_TYPE_WPA,
diff --git a/drivers/net/wireless/ath/ath12k/wifi8/dp_rx.c b/drivers/net/wireless/ath/ath12k/wifi8/dp_rx.c
index 632ce30fe459..14117430c43b 100644
--- a/drivers/net/wireless/ath/ath12k/wifi8/dp_rx.c
+++ b/drivers/net/wireless/ath/ath12k/wifi8/dp_rx.c
@@ -50,6 +50,73 @@ ath12k_wifi8_get_sw_desc(struct ath12k_dp *dp, const struct hal_reo_dest_ring *d
return ath12k_dp_get_rx_desc(dp, cookie);
}
+static inline bool ath12k_wifi8_dp_reo_cmd_shutdown(struct ath12k_base *ab)
+{
+ struct ath12k_dp *dp = ath12k_ab_to_dp(ab);
+ struct ath12k_dp *central_dp = ath12k_get_central_dp(dp);
+ struct ath12k_base *central_ab = central_dp->ab;
+
+ return test_bit(ATH12K_FLAG_CRASH_FLUSH, ¢ral_ab->dev_flags) ||
+ test_bit(ATH12K_FLAG_RECOVERY, ¢ral_ab->dev_flags);
+}
+
+static int ath12k_wifi8_dp_reo_cmd_prepare(struct ath12k_dp_rx_reo_cmd **dp_cmd,
+ void *data, size_t len,
+ void (*cb)(struct ath12k_dp *dp,
+ void *ctx,
+ struct hal_reo_status *status))
+{
+ if (!cb) {
+ *dp_cmd = NULL;
+ return 0;
+ }
+
+ if (WARN_ON(len > sizeof((*dp_cmd)->u)))
+ return -EINVAL;
+
+ if (WARN_ON(len && !data))
+ return -EINVAL;
+
+ *dp_cmd = kzalloc_obj(struct ath12k_dp_rx_reo_cmd, GFP_ATOMIC);
+ if (!*dp_cmd)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&(*dp_cmd)->list);
+ if (len)
+ memcpy(&(*dp_cmd)->u, data, len);
+ (*dp_cmd)->handler = cb;
+
+ return 0;
+}
+
+static void ath12k_wifi8_dp_reo_cmd_queue_locked(struct ath12k_dp *dp,
+ struct ath12k_dp_rx_reo_cmd *dp_cmd,
+ int cmd_num)
+{
+ if (!dp_cmd)
+ return;
+
+ lockdep_assert_held(&dp->reo_cmd_lock);
+
+ dp_cmd->cmd_num = cmd_num;
+ list_add_tail(&dp_cmd->list, &dp->reo_cmd_list);
+}
+
+static int ath12k_wifi8_dp_reo_cmd_validate_batch(struct ath12k_reo_cmd_entry *cmds,
+ int n)
+{
+ int i;
+
+ for (i = 0; i < n; i++) {
+ if (!(cmds[i].cmd.flag & HAL_REO_CMD_FLG_FLUSH_BLOCK_LATER))
+ continue;
+
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
static int ath12k_wifi8_dp_reo_cmd_send_ring(struct ath12k_base *ab,
struct hal_srng *cmd_ring,
void *data, size_t len,
@@ -63,25 +130,14 @@ static int ath12k_wifi8_dp_reo_cmd_send_ring(struct ath12k_base *ab,
int cmd_num;
struct ath12k_dp *central_dp = ath12k_get_central_dp(dp);
struct ath12k_base *central_ab = central_dp->ab;
+ int ret;
- if (test_bit(ATH12K_FLAG_CRASH_FLUSH, ¢ral_ab->dev_flags) ||
- test_bit(ATH12K_FLAG_RECOVERY, ¢ral_ab->dev_flags))
+ if (ath12k_wifi8_dp_reo_cmd_shutdown(ab))
return -ESHUTDOWN;
- if (!cb) {
- dp_cmd = NULL;
- } else {
- if (WARN_ON(len > sizeof(dp_cmd->u)))
- return -EINVAL;
-
- dp_cmd = kzalloc_obj(*dp_cmd, GFP_ATOMIC);
- if (!dp_cmd)
- return -ENOMEM;
-
- INIT_LIST_HEAD(&dp_cmd->list);
- memcpy(&dp_cmd->u, data, len);
- dp_cmd->handler = cb;
- }
+ ret = ath12k_wifi8_dp_reo_cmd_prepare(&dp_cmd, data, len, cb);
+ if (ret)
+ return ret;
cmd_num = ath12k_wifi8_hal_reo_cmd_send(central_ab, cmd_ring, type, cmd);
if (cmd_num < 0) {
@@ -97,15 +153,82 @@ static int ath12k_wifi8_dp_reo_cmd_send_ring(struct ath12k_base *ab,
if (!dp_cmd)
return 0;
- dp_cmd->cmd_num = cmd_num;
-
spin_lock_bh(¢ral_dp->reo_cmd_lock);
- list_add_tail(&dp_cmd->list, ¢ral_dp->reo_cmd_list);
+ ath12k_wifi8_dp_reo_cmd_queue_locked(central_dp, dp_cmd, cmd_num);
spin_unlock_bh(¢ral_dp->reo_cmd_lock);
return 0;
}
+static int
+ath12k_wifi8_dp_reo_cmd_send_ring_n(struct ath12k_base *ab,
+ struct hal_srng *cmd_ring,
+ struct ath12k_reo_cmd_entry *entries,
+ struct ath12k_reo_dp_cmd_desc *dp_descs,
+ int n)
+{
+ struct ath12k_dp *dp = ath12k_ab_to_dp(ab);
+ struct ath12k_dp *central_dp = ath12k_get_central_dp(dp);
+ struct ath12k_base *central_ab = central_dp->ab;
+ struct ath12k_dp_rx_reo_cmd **dp_cmds;
+ int i, ret;
+
+ if (WARN_ON(n <= 0))
+ return -EINVAL;
+
+ if (WARN_ON(!entries || !dp_descs))
+ return -EINVAL;
+
+ if (ath12k_wifi8_dp_reo_cmd_shutdown(ab))
+ return -ESHUTDOWN;
+
+ ret = ath12k_wifi8_dp_reo_cmd_validate_batch(entries, n);
+ if (ret)
+ return ret;
+
+ dp_cmds = kcalloc(n, sizeof(*dp_cmds), GFP_ATOMIC);
+ if (!dp_cmds)
+ return -ENOMEM;
+
+ for (i = 0; i < n; i++) {
+ ret = ath12k_wifi8_dp_reo_cmd_prepare(&dp_cmds[i],
+ dp_descs[i].data,
+ dp_descs[i].len,
+ dp_descs[i].cb);
+ if (ret)
+ goto err_free;
+ }
+
+ spin_lock_bh(&cmd_ring->lock);
+ ath12k_hal_srng_access_begin(central_ab, cmd_ring);
+
+ ret = ath12k_wifi8_hal_reo_cmd_send_n_locked(central_ab, cmd_ring,
+ entries, n);
+ if (!ret) {
+ spin_lock_bh(¢ral_dp->reo_cmd_lock);
+ for (i = 0; i < n; i++)
+ ath12k_wifi8_dp_reo_cmd_queue_locked(central_dp, dp_cmds[i],
+ entries[i].cmd_num);
+ spin_unlock_bh(¢ral_dp->reo_cmd_lock);
+ }
+
+ ath12k_hal_srng_access_end(central_ab, cmd_ring);
+ spin_unlock_bh(&cmd_ring->lock);
+
+ if (ret)
+ goto err_free;
+
+ kfree(dp_cmds);
+ return 0;
+
+err_free:
+ for (i = 0; i < n; i++)
+ kfree(dp_cmds[i]);
+ kfree(dp_cmds);
+
+ return ret;
+}
+
int ath12k_wifi8_dp_reo_cmd_send(struct ath12k_base *ab,
void *data, size_t len,
enum hal_reo_cmd_type type,
@@ -116,8 +239,9 @@ int ath12k_wifi8_dp_reo_cmd_send(struct ath12k_base *ab,
struct ath12k_dp *dp = ath12k_ab_to_dp(ab);
struct ath12k_dp *central_dp = ath12k_get_central_dp(dp);
struct hal_srng *cmd_ring;
+ u32 ring_id = central_dp->reo_cmd_ring.ring_id;
- cmd_ring = ¢ral_dp->ab->hal.srng_list[central_dp->reo_cmd_ring.ring_id];
+ cmd_ring = ¢ral_dp->ab->hal.srng_list[ring_id];
return ath12k_wifi8_dp_reo_cmd_send_ring(ab, cmd_ring, data, len,
type, cmd, cb);
@@ -134,15 +258,31 @@ int ath12k_wifi8_dp_reo_cmd_send_highprio(struct ath12k_base *ab,
struct ath12k_dp *central_dp = ath12k_get_central_dp(dp);
struct ath12k_dp_wifi8 *dp_wifi8 = ath12k_get_dp_wifi8(central_dp);
struct hal_srng *cmd_ring;
- u32 ring_id;
+ u32 ring_id = dp_wifi8->reo_high_prio_cmd_ring.ring_id;
- ring_id = dp_wifi8->reo_high_prio_cmd_ring.ring_id;
cmd_ring = ¢ral_dp->ab->hal.srng_list[ring_id];
return ath12k_wifi8_dp_reo_cmd_send_ring(ab, cmd_ring, data, len,
type, cmd, cb);
}
+int ath12k_wifi8_dp_reo_cmd_send_highprio_n(struct ath12k_base *ab,
+ struct ath12k_reo_cmd_entry *entries,
+ struct ath12k_reo_dp_cmd_desc *dp_descs,
+ int n)
+{
+ struct ath12k_dp *dp = ath12k_ab_to_dp(ab);
+ struct ath12k_dp *central_dp = ath12k_get_central_dp(dp);
+ struct ath12k_dp_wifi8 *dp_wifi8 = ath12k_get_dp_wifi8(central_dp);
+ struct hal_srng *cmd_ring;
+ u32 ring_id = dp_wifi8->reo_high_prio_cmd_ring.ring_id;
+
+ cmd_ring = ¢ral_dp->ab->hal.srng_list[ring_id];
+
+ return ath12k_wifi8_dp_reo_cmd_send_ring_n(ab, cmd_ring, entries,
+ dp_descs, n);
+}
+
/**
* ath12k_wifi8_rx_spd_buf_alloc - allocate per-ring SPD scratch-pad arrays
* @dp_hw_grp_wifi8: Wi-Fi 8 hardware-group private data
diff --git a/drivers/net/wireless/ath/ath12k/wifi8/dp_rx.h b/drivers/net/wireless/ath/ath12k/wifi8/dp_rx.h
index 10940dd92a62..2fb56776adb0 100644
--- a/drivers/net/wireless/ath/ath12k/wifi8/dp_rx.h
+++ b/drivers/net/wireless/ath/ath12k/wifi8/dp_rx.h
@@ -12,6 +12,7 @@
struct ath12k_dp_hw_group_wifi8;
struct hal_reo_status;
+struct ath12k_reo_cmd_entry;
struct hal_rx_mpdu_desc_info {
union {
@@ -159,6 +160,17 @@ int ath12k_wifi8_dp_reo_cmd_send_highprio(struct ath12k_base *ab,
struct ath12k_hal_reo_cmd *cmd,
void (*cb)(struct ath12k_dp *dp, void *ctx,
struct hal_reo_status *status));
+struct ath12k_reo_dp_cmd_desc {
+ void *data;
+ size_t len;
+ void (*cb)(struct ath12k_dp *dp, void *ctx,
+ struct hal_reo_status *status);
+};
+
+int ath12k_wifi8_dp_reo_cmd_send_highprio_n(struct ath12k_base *ab,
+ struct ath12k_reo_cmd_entry *entries,
+ struct ath12k_reo_dp_cmd_desc *dp_descs,
+ int n);
int ath12k_wifi8_dp_rx_ring_setup(struct ath12k_base *ab);
int ath12k_wifi8_dp_rx_ring_free(struct ath12k_base *ab);
int ath12k_wifi8_dp_rxdma_ring_sel_config_qcn9625(struct ath12k_base *ab);
diff --git a/drivers/net/wireless/ath/ath12k/wifi8/hal_rx.c b/drivers/net/wireless/ath/ath12k/wifi8/hal_rx.c
index d78c178cc70d..12a37c395bb0 100644
--- a/drivers/net/wireless/ath/ath12k/wifi8/hal_rx.c
+++ b/drivers/net/wireless/ath/ath12k/wifi8/hal_rx.c
@@ -536,24 +536,116 @@ static int ath12k_hal_reo_cmd_unblock_cache(struct hal_tlv_64_hdr *tlv,
return le16_to_cpu(desc->cmd.reo_cmd_number);
}
-int ath12k_wifi8_hal_reo_cmd_send(struct ath12k_base *ab, struct hal_srng *srng,
- enum hal_reo_cmd_type type,
- struct ath12k_hal_reo_cmd *cmd)
+static int ath12k_wifi8_hal_reo_cmd_fill(struct ath12k_base *ab,
+ struct hal_tlv_64_hdr *reo_desc,
+ enum hal_reo_cmd_type type,
+ struct ath12k_hal_reo_cmd *cmd)
{
- struct hal_tlv_64_hdr *reo_desc;
- u32 saved_hp, saved_reap_hp;
- int ret;
-
- if (type == HAL_REO_CMD_FLUSH_TIMEOUT_LIST) {
+ switch (type) {
+ case HAL_REO_CMD_GET_QUEUE_STATS:
+ return ath12k_wifi8_hal_reo_cmd_queue_stats(reo_desc, cmd);
+ case HAL_REO_CMD_FLUSH_CACHE:
+ return ath12k_wifi8_hal_reo_cmd_flush_cache(&ab->hal, reo_desc,
+ cmd);
+ case HAL_REO_CMD_UPDATE_RX_QUEUE:
+ return ath12k_wifi8_hal_reo_cmd_update_rx_queue(reo_desc, cmd);
+ case HAL_REO_CMD_FLUSH_QUEUE:
+ return ath12k_hal_reo_cmd_flush_queue(reo_desc, cmd);
+ case HAL_REO_CMD_UNBLOCK_CACHE:
+ return ath12k_hal_reo_cmd_unblock_cache(reo_desc, cmd);
+ case HAL_REO_CMD_FLUSH_TIMEOUT_LIST:
ath12k_warn(ab, "Unsupported reo command %d\n", type);
return -EOPNOTSUPP;
+ default:
+ ath12k_warn(ab, "Unknown reo command %d\n", type);
+ return -EINVAL;
}
+}
- if (type > HAL_REO_CMD_UPDATE_RX_QUEUE) {
- ath12k_warn(ab, "Unknown reo command %d\n", type);
+int ath12k_wifi8_hal_reo_cmd_send_n_locked(struct ath12k_base *ab,
+ struct hal_srng *srng,
+ struct ath12k_reo_cmd_entry *entries,
+ int n)
+{
+ struct hal_tlv_64_hdr *reo_desc;
+ u32 saved_hp, saved_reap_hp;
+ int i, ret;
+
+ if (WARN_ON(n <= 0))
+ return -EINVAL;
+
+ if (WARN_ON(!entries))
return -EINVAL;
+
+ lockdep_assert_held(&srng->lock);
+
+ saved_hp = srng->u.src_ring.hp;
+ saved_reap_hp = srng->u.src_ring.reap_hp;
+
+ for (i = 0; i < n; i++) {
+ reo_desc = ath12k_hal_srng_src_get_next_entry(ab, srng);
+ if (!reo_desc) {
+ ret = -ENOBUFS;
+ goto err_restore_hp;
+ }
+
+ ret = ath12k_wifi8_hal_reo_cmd_fill(ab, reo_desc,
+ entries[i].type,
+ &entries[i].cmd);
+ if (ret <= 0) {
+ if (!ret)
+ ret = -EINVAL;
+ goto err_restore_hp;
+ }
+
+ entries[i].cmd_num = ret;
}
+ return 0;
+
+err_restore_hp:
+ /* Keep batched command submission all-or-nothing. If any descriptor
+ * cannot be reserved or formatted, roll the source ring pointers back
+ * so partially prepared entries are not exposed to hardware.
+ */
+ srng->u.src_ring.hp = saved_hp;
+ srng->u.src_ring.reap_hp = saved_reap_hp;
+
+ return ret;
+}
+
+int ath12k_wifi8_hal_reo_cmd_send_n(struct ath12k_base *ab,
+ struct hal_srng *srng,
+ struct ath12k_reo_cmd_entry *entries,
+ int n)
+{
+ int ret;
+
+ if (WARN_ON(n <= 0))
+ return -EINVAL;
+
+ if (WARN_ON(!entries))
+ return -EINVAL;
+
+ spin_lock_bh(&srng->lock);
+ ath12k_hal_srng_access_begin(ab, srng);
+
+ ret = ath12k_wifi8_hal_reo_cmd_send_n_locked(ab, srng, entries, n);
+
+ ath12k_hal_srng_access_end(ab, srng);
+ spin_unlock_bh(&srng->lock);
+
+ return ret;
+}
+
+int ath12k_wifi8_hal_reo_cmd_send(struct ath12k_base *ab, struct hal_srng *srng,
+ enum hal_reo_cmd_type type,
+ struct ath12k_hal_reo_cmd *cmd)
+{
+ struct hal_tlv_64_hdr *reo_desc;
+ u32 saved_hp, saved_reap_hp;
+ int ret;
+
spin_lock_bh(&srng->lock);
ath12k_hal_srng_access_begin(ab, srng);
@@ -566,26 +658,12 @@ int ath12k_wifi8_hal_reo_cmd_send(struct ath12k_base *ab, struct hal_srng *srng,
goto out;
}
- switch (type) {
- case HAL_REO_CMD_GET_QUEUE_STATS:
- ret = ath12k_wifi8_hal_reo_cmd_queue_stats(reo_desc, cmd);
- break;
- case HAL_REO_CMD_FLUSH_CACHE:
- ret = ath12k_wifi8_hal_reo_cmd_flush_cache(&ab->hal, reo_desc,
- cmd);
- break;
- case HAL_REO_CMD_UPDATE_RX_QUEUE:
- ret = ath12k_wifi8_hal_reo_cmd_update_rx_queue(reo_desc, cmd);
- break;
- case HAL_REO_CMD_FLUSH_QUEUE:
- ret = ath12k_hal_reo_cmd_flush_queue(reo_desc, cmd);
- break;
- case HAL_REO_CMD_UNBLOCK_CACHE:
- ret = ath12k_hal_reo_cmd_unblock_cache(reo_desc, cmd);
- break;
- default:
- ret = -EINVAL;
- break;
+ ret = ath12k_wifi8_hal_reo_cmd_fill(ab, reo_desc, type, cmd);
+ if (ret <= 0) {
+ if (!ret)
+ ret = -EINVAL;
+ srng->u.src_ring.hp = saved_hp;
+ srng->u.src_ring.reap_hp = saved_reap_hp;
}
out:
diff --git a/drivers/net/wireless/ath/ath12k/wifi8/hal_rx.h b/drivers/net/wireless/ath/ath12k/wifi8/hal_rx.h
index 357839c7d9e3..430b0f07563f 100644
--- a/drivers/net/wireless/ath/ath12k/wifi8/hal_rx.h
+++ b/drivers/net/wireless/ath/ath12k/wifi8/hal_rx.h
@@ -100,6 +100,7 @@ struct ath12k_hal_rx_cmd_ring_param {
struct hal_tlv_64_hdr;
struct hal_reo_status;
+struct ath12k_reo_cmd_entry;
int
ath12k_wifi8_hal_invalidate_rx_cache_cmd_send(struct ath12k_base *ab,
@@ -108,6 +109,14 @@ ath12k_wifi8_hal_invalidate_rx_cache_cmd_send(struct ath12k_base *ab,
int ath12k_wifi8_hal_reo_cmd_send(struct ath12k_base *ab, struct hal_srng *srng,
enum hal_reo_cmd_type type,
struct ath12k_hal_reo_cmd *cmd);
+int ath12k_wifi8_hal_reo_cmd_send_n_locked(struct ath12k_base *ab,
+ struct hal_srng *srng,
+ struct ath12k_reo_cmd_entry *entries,
+ int n);
+int ath12k_wifi8_hal_reo_cmd_send_n(struct ath12k_base *ab,
+ struct hal_srng *srng,
+ struct ath12k_reo_cmd_entry *entries,
+ int n);
void ath12k_wifi8_hal_reo_hw_setup(struct ath12k_base *ab);
void ath12k_wifi8_hal_reo_status_queue_stats(struct ath12k_base *ab,
struct hal_tlv_64_hdr *tlv,
--
2.43.0