diff mbox series

[3/3] ath11k: process full monitor mode rx support

Message ID 20210721171905.61838-4-jouni@codeaurora.org (mailing list archive)
State Changes Requested
Delegated to: Kalle Valo
Headers show
Series ath11k: add full monitor mode support for QCN9074 | expand

Commit Message

Jouni Malinen July 21, 2021, 5:19 p.m. UTC
From: Anilkumar Kolli <akolli@codeaurora.org>

In full monitor mode, monitor destination ring is read before monitor
status ring. mon_dst_ring has ppdu id, reap till the end of PPDU. Add
all the MPDUs to list. Start processing the status ring, if PPDU id in
status ring is lagging behind, reap the status ring, once the PPDU ID
matches, deliver the MSDU to upper layer. If status PPDU id leading,
reap the mon_dst_ring. The advantage with full monitor mode is hardware
has status buffers available for all the MPDUs in mon_dst_ring.

Tested-on: QCN9074 hw1.0 PCI WLAN.HK.2.4.0.1-01734-QCAHKSWPL_SILICONZ-1

Signed-off-by: Anilkumar Kolli <akolli@codeaurora.org>
Signed-off-by: Jouni Malinen <jouni@codeaurora.org>
---
 drivers/net/wireless/ath/ath11k/dp.c    |   1 +
 drivers/net/wireless/ath/ath11k/dp.h    |  26 +-
 drivers/net/wireless/ath/ath11k/dp_rx.c | 413 +++++++++++++++++++++++-
 3 files changed, 436 insertions(+), 4 deletions(-)

Comments

Kalle Valo Nov. 12, 2021, 7:52 a.m. UTC | #1
Jouni Malinen <jouni@codeaurora.org> wrote:

> From: Anilkumar Kolli <akolli@codeaurora.org>
> 
> In full monitor mode, monitor destination ring is read before monitor
> status ring. mon_dst_ring has ppdu id, reap till the end of PPDU. Add
> all the MPDUs to list. Start processing the status ring, if PPDU id in
> status ring is lagging behind, reap the status ring, once the PPDU ID
> matches, deliver the MSDU to upper layer. If status PPDU id leading,
> reap the mon_dst_ring. The advantage with full monitor mode is hardware
> has status buffers available for all the MPDUs in mon_dst_ring.
> 
> Tested-on: QCN9074 hw1.0 PCI WLAN.HK.2.4.0.1-01734-QCAHKSWPL_SILICONZ-1
> 
> Signed-off-by: Anilkumar Kolli <akolli@codeaurora.org>
> Signed-off-by: Jouni Malinen <jouni@codeaurora.org>

I changed the commit log to this to better describe how this patch helps:

    ath11k: process full monitor mode rx support
    
    In full monitor mode, monitor destination ring is read before monitor
    status ring. mon_dst_ring has ppdu id, reap till the end of PPDU. Add
    all the MPDUs to list. Start processing the status ring, if PPDU id in
    status ring is lagging behind, reap the status ring, once the PPDU ID
    matches, deliver the MSDU to upper layer. If status PPDU id leading,
    reap the mon_dst_ring.
    
    The advantage with full monitor mode is hardware has status buffers available
    for all the MPDUs in mon_dst_ring, which makes it possible to deliver more
    frames to be seen on sniffer.
    
    Tested-on: QCN9074 hw1.0 PCI WLAN.HK.2.4.0.1-01734-QCAHKSWPL_SILICONZ-1

But now I see a new warning with QCA6390 caused by patch 3. Please fix that and submit v2.

[  108.354361] ath11k_pci 0000:06:00.0: chip_id 0x0 chip_family 0xb board_id 0xff soc_id 0xffffffff
[  108.354547] ath11k_pci 0000:06:00.0: fw_version 0x101c06cc fw_build_timestamp 2020-06-24 19:50 fw_build_id 
[  110.995359] INFO: trying to register non-static key.
[  110.995429] The code is fine but needs lockdep annotation, or maybe
[  110.995489] you didn't initialize this object before use?
[  110.995546] turning off the locking correctness validator.
[  110.995604] CPU: 1 PID: 0 Comm: swapper/1 Kdump: loaded Tainted: G        W         5.15.0-wt-ath+ #522
[  110.995664] Hardware name: Intel(R) Client Systems NUC8i7HVK/NUC8i7HVB, BIOS HNKBLi70.86A.0067.2021.0528.1339 05/28/2021
[  110.995720] Call Trace:
[  110.995774]  <IRQ>
[  110.995828]  dump_stack_lvl+0x57/0x7d
[  110.995887]  register_lock_class+0x1604/0x17e0
[  110.995945]  ? check_prev_add+0x7bb/0x20f0
[  110.996002]  ? is_dynamic_key+0x1a0/0x1a0
[  110.996058]  ? validate_chain+0xac3/0x1b30
[  110.996127]  __lock_acquire+0x96/0x1870
[  110.996196]  ? check_prev_add+0x20f0/0x20f0
[  110.996266]  ? sched_clock_cpu+0x15/0x1b0
[  110.996339]  ? mark_lock+0xd0/0x14a0
[  110.996401]  lock_acquire.part.0+0x117/0x340
[  110.996470]  ? ath11k_dp_rx_mon_dest_process.constprop.0+0x13a/0x6e0 [ath11k]
[  110.996553]  ? rcu_read_unlock+0x40/0x40
[  110.996622]  ? rcu_read_lock_sched_held+0x3a/0x70
[  110.996685]  ? lock_acquire+0x1a8/0x210
[  110.996751]  ? ath11k_dp_rx_mon_dest_process.constprop.0+0x13a/0x6e0 [ath11k]
[  110.996831]  _raw_spin_lock_bh+0x34/0x80
[  110.996892]  ? ath11k_dp_rx_mon_dest_process.constprop.0+0x13a/0x6e0 [ath11k]
[  110.996971]  ath11k_dp_rx_mon_dest_process.constprop.0+0x13a/0x6e0 [ath11k]
[  110.997071]  ? ath11k_dp_rxbufs_replenish+0x7a0/0x7a0 [ath11k]
[  110.997187]  ? mark_held_locks+0xa5/0xe0
[  110.997272]  ? ath11k_hal_rx_parse_mon_status_tlv+0x6d7/0x1880 [ath11k]
[  110.997389]  ? ath11k_hal_rx_parse_mon_status+0xed/0x170 [ath11k]
[  110.997506]  ath11k_dp_rx_process_mon_rings+0x3f9/0x580 [ath11k]
[  110.997600]  ? ath11k_dp_rx_pdev_alloc+0xd50/0xd50 [ath11k]
[  110.997680]  ath11k_dp_service_mon_ring+0x67/0xe0 [ath11k]
[  110.997759]  ? ath11k_dp_rx_process_mon_rings+0x580/0x580 [ath11k]
[  110.997835]  call_timer_fn+0x167/0x4a0
[  110.997903]  ? add_timer_on+0x3b0/0x3b0
[  110.997967]  ? lockdep_hardirqs_on_prepare.part.0+0x18c/0x370
[  110.998031]  __run_timers.part.0+0x539/0x8b0
[  110.998123]  ? ath11k_dp_rx_process_mon_rings+0x580/0x580 [ath11k]
[  110.998228]  ? call_timer_fn+0x4a0/0x4a0
[  110.998314]  ? clockevents_program_event+0xdd/0x280
[  110.998408]  ? recalibrate_cpu_khz+0x10/0x10
[  110.998496]  ? ktime_get+0x119/0x2f0
[  110.998579]  ? hrtimer_interrupt+0x32f/0x770
[  110.998679]  run_timer_softirq+0x97/0x180
[  110.998764]  __do_softirq+0x276/0x86a
[  110.998850]  __irq_exit_rcu+0x11c/0x180
[  110.998934]  irq_exit_rcu+0x5/0x20
[  110.999017]  sysvec_apic_timer_interrupt+0x8e/0xc0
[  110.999100]  </IRQ>
[  110.999181]  <TASK>
[  110.999261]  asm_sysvec_apic_timer_interrupt+0x12/0x20
Anilkumar Kolli Dec. 8, 2021, 1:13 p.m. UTC | #2
On 2021-11-12 13:22, Kalle Valo wrote:
> Jouni Malinen <jouni@codeaurora.org> wrote:
> 
>> From: Anilkumar Kolli <akolli@codeaurora.org>
>> 
>> In full monitor mode, monitor destination ring is read before monitor
>> status ring. mon_dst_ring has ppdu id, reap till the end of PPDU. Add
>> all the MPDUs to list. Start processing the status ring, if PPDU id in
>> status ring is lagging behind, reap the status ring, once the PPDU ID
>> matches, deliver the MSDU to upper layer. If status PPDU id leading,
>> reap the mon_dst_ring. The advantage with full monitor mode is 
>> hardware
>> has status buffers available for all the MPDUs in mon_dst_ring.
>> 
>> Tested-on: QCN9074 hw1.0 PCI 
>> WLAN.HK.2.4.0.1-01734-QCAHKSWPL_SILICONZ-1
>> 
>> Signed-off-by: Anilkumar Kolli <akolli@codeaurora.org>
>> Signed-off-by: Jouni Malinen <jouni@codeaurora.org>
> 
> I changed the commit log to this to better describe how this patch 
> helps:
> 
>     ath11k: process full monitor mode rx support
> 
>     In full monitor mode, monitor destination ring is read before 
> monitor
>     status ring. mon_dst_ring has ppdu id, reap till the end of PPDU. 
> Add
>     all the MPDUs to list. Start processing the status ring, if PPDU id 
> in
>     status ring is lagging behind, reap the status ring, once the PPDU 
> ID
>     matches, deliver the MSDU to upper layer. If status PPDU id 
> leading,
>     reap the mon_dst_ring.
> 
>     The advantage with full monitor mode is hardware has status
> buffers available
>     for all the MPDUs in mon_dst_ring, which makes it possible to 
> deliver more
>     frames to be seen on sniffer.
> 
>     Tested-on: QCN9074 hw1.0 PCI 
> WLAN.HK.2.4.0.1-01734-QCAHKSWPL_SILICONZ-1
> 
> But now I see a new warning with QCA6390 caused by patch 3. Please fix
> that and submit v2.
> 
> [  108.354361] ath11k_pci 0000:06:00.0: chip_id 0x0 chip_family 0xb
> board_id 0xff soc_id 0xffffffff
> [  108.354547] ath11k_pci 0000:06:00.0: fw_version 0x101c06cc
> fw_build_timestamp 2020-06-24 19:50 fw_build_id
> [  110.995359] INFO: trying to register non-static key.
> [  110.995429] The code is fine but needs lockdep annotation, or maybe
> [  110.995489] you didn't initialize this object before use?
> [  110.995546] turning off the locking correctness validator.
> [  110.995604] CPU: 1 PID: 0 Comm: swapper/1 Kdump: loaded Tainted: G
>       W         5.15.0-wt-ath+ #522
> [  110.995664] Hardware name: Intel(R) Client Systems
> NUC8i7HVK/NUC8i7HVB, BIOS HNKBLi70.86A.0067.2021.0528.1339 05/28/2021
> [  110.995720] Call Trace:
> [  110.995774]  <IRQ>
> [  110.995828]  dump_stack_lvl+0x57/0x7d
> [  110.995887]  register_lock_class+0x1604/0x17e0
> [  110.995945]  ? check_prev_add+0x7bb/0x20f0
> [  110.996002]  ? is_dynamic_key+0x1a0/0x1a0
> [  110.996058]  ? validate_chain+0xac3/0x1b30
> [  110.996127]  __lock_acquire+0x96/0x1870
> [  110.996196]  ? check_prev_add+0x20f0/0x20f0
> [  110.996266]  ? sched_clock_cpu+0x15/0x1b0
> [  110.996339]  ? mark_lock+0xd0/0x14a0
> [  110.996401]  lock_acquire.part.0+0x117/0x340
> [  110.996470]  ? ath11k_dp_rx_mon_dest_process.constprop.0+0x13a/0x6e0 
> [ath11k]
> [  110.996553]  ? rcu_read_unlock+0x40/0x40
> [  110.996622]  ? rcu_read_lock_sched_held+0x3a/0x70
> [  110.996685]  ? lock_acquire+0x1a8/0x210
> [  110.996751]  ? ath11k_dp_rx_mon_dest_process.constprop.0+0x13a/0x6e0 
> [ath11k]
> [  110.996831]  _raw_spin_lock_bh+0x34/0x80
> [  110.996892]  ? ath11k_dp_rx_mon_dest_process.constprop.0+0x13a/0x6e0 
> [ath11k]
> [  110.996971]  ath11k_dp_rx_mon_dest_process.constprop.0+0x13a/0x6e0 
> [ath11k]
> [  110.997071]  ? ath11k_dp_rxbufs_replenish+0x7a0/0x7a0 [ath11k]
> [  110.997187]  ? mark_held_locks+0xa5/0xe0
> [  110.997272]  ? ath11k_hal_rx_parse_mon_status_tlv+0x6d7/0x1880 
> [ath11k]
> [  110.997389]  ? ath11k_hal_rx_parse_mon_status+0xed/0x170 [ath11k]
> [  110.997506]  ath11k_dp_rx_process_mon_rings+0x3f9/0x580 [ath11k]
> [  110.997600]  ? ath11k_dp_rx_pdev_alloc+0xd50/0xd50 [ath11k]
> [  110.997680]  ath11k_dp_service_mon_ring+0x67/0xe0 [ath11k]
> [  110.997759]  ? ath11k_dp_rx_process_mon_rings+0x580/0x580 [ath11k]
> [  110.997835]  call_timer_fn+0x167/0x4a0
> [  110.997903]  ? add_timer_on+0x3b0/0x3b0
> [  110.997967]  ? lockdep_hardirqs_on_prepare.part.0+0x18c/0x370
> [  110.998031]  __run_timers.part.0+0x539/0x8b0
> [  110.998123]  ? ath11k_dp_rx_process_mon_rings+0x580/0x580 [ath11k]
> [  110.998228]  ? call_timer_fn+0x4a0/0x4a0
> [  110.998314]  ? clockevents_program_event+0xdd/0x280
> [  110.998408]  ? recalibrate_cpu_khz+0x10/0x10
> [  110.998496]  ? ktime_get+0x119/0x2f0
> [  110.998579]  ? hrtimer_interrupt+0x32f/0x770
> [  110.998679]  run_timer_softirq+0x97/0x180
> [  110.998764]  __do_softirq+0x276/0x86a
> [  110.998850]  __irq_exit_rcu+0x11c/0x180
> [  110.998934]  irq_exit_rcu+0x5/0x20
> [  110.999017]  sysvec_apic_timer_interrupt+0x8e/0xc0
> [  110.999100]  </IRQ>
> [  110.999181]  <TASK>
> [  110.999261]  asm_sysvec_apic_timer_interrupt+0x12/0x20

I have posted v2 patch. With v2, I did not see the issue with QCA6390 
during the bootup.

Thanks
Anil
diff mbox series

Patch

diff --git a/drivers/net/wireless/ath/ath11k/dp.c b/drivers/net/wireless/ath/ath11k/dp.c
index b0c8f6290099..dd69ba8f7d80 100644
--- a/drivers/net/wireless/ath/ath11k/dp.c
+++ b/drivers/net/wireless/ath/ath11k/dp.c
@@ -1021,6 +1021,7 @@  int ath11k_dp_alloc(struct ath11k_base *ab)
 
 	INIT_LIST_HEAD(&dp->reo_cmd_list);
 	INIT_LIST_HEAD(&dp->reo_cmd_cache_flush_list);
+	INIT_LIST_HEAD(&dp->dp_full_mon_mpdu_list);
 	spin_lock_init(&dp->reo_cmd_lock);
 
 	dp->reo_cmd_cache_flush_count = 0;
diff --git a/drivers/net/wireless/ath/ath11k/dp.h b/drivers/net/wireless/ath/ath11k/dp.h
index b39a6652d631..69b75bc3312f 100644
--- a/drivers/net/wireless/ath/ath11k/dp.h
+++ b/drivers/net/wireless/ath/ath11k/dp.h
@@ -88,6 +88,19 @@  struct dp_tx_ring {
 	int tx_status_tail;
 };
 
+enum dp_mon_status_buf_state {
+	/* PPDU id matches in dst ring and status ring */
+	DP_MON_STATUS_MATCH,
+	/* status ring dma is not done */
+	DP_MON_STATUS_NO_DMA,
+	/* status ring is lagging, reap status ring */
+	DP_MON_STATUS_LAG,
+	/* status ring is leading, reap dst ring and drop */
+	DP_MON_STATUS_LEAD,
+	/* replinish monitor status ring */
+	DP_MON_STATUS_REPLINISH,
+};
+
 struct ath11k_pdev_mon_stats {
 	u32 status_ppdu_state;
 	u32 status_ppdu_start;
@@ -103,6 +116,12 @@  struct ath11k_pdev_mon_stats {
 	u32 dup_mon_buf_cnt;
 };
 
+struct dp_full_mon_mpdu {
+	struct list_head list;
+	struct sk_buff *head;
+	struct sk_buff *tail;
+};
+
 struct dp_link_desc_bank {
 	void *vaddr_unaligned;
 	void *vaddr;
@@ -134,7 +153,11 @@  struct ath11k_mon_data {
 	u32 mon_last_buf_cookie;
 	u64 mon_last_linkdesc_paddr;
 	u16 chan_noise_floor;
-
+	bool hold_mon_dst_ring;
+	enum dp_mon_status_buf_state buf_state;
+	dma_addr_t mon_status_paddr;
+	struct dp_full_mon_mpdu *mon_mpdu;
+	struct hal_sw_mon_ring_entries sw_mon_entries;
 	struct ath11k_pdev_mon_stats rx_mon_stats;
 	/* lock for monitor data */
 	spinlock_t mon_lock;
@@ -242,6 +265,7 @@  struct ath11k_dp {
 	struct hal_wbm_idle_scatter_list scatter_list[DP_IDLE_SCATTER_BUFS_MAX];
 	struct list_head reo_cmd_list;
 	struct list_head reo_cmd_cache_flush_list;
+	struct list_head dp_full_mon_mpdu_list;
 	u32 reo_cmd_cache_flush_count;
 	/**
 	 * protects access to below fields,
diff --git a/drivers/net/wireless/ath/ath11k/dp_rx.c b/drivers/net/wireless/ath/ath11k/dp_rx.c
index 6fde70914e1a..587690bec595 100644
--- a/drivers/net/wireless/ath/ath11k/dp_rx.c
+++ b/drivers/net/wireless/ath/ath11k/dp_rx.c
@@ -2863,11 +2863,49 @@  int ath11k_dp_rx_mon_status_bufs_replenish(struct ath11k_base *ab, int mac_id,
 	return req_entries - num_remain;
 }
 
+#define ATH11K_DP_RX_FULL_MON_PPDU_ID_WRAP 32535
+
+static void
+ath11k_dp_rx_mon_update_status_buf_state(struct ath11k_mon_data *pmon,
+					 struct hal_tlv_hdr *tlv)
+{
+	struct hal_rx_ppdu_start *ppdu_start;
+	u16 ppdu_id_diff, ppdu_id, tlv_len;
+	u8 *ptr;
+
+	/* PPDU id is part of second tlv, move ptr to second tlv */
+	tlv_len = FIELD_GET(HAL_TLV_HDR_LEN, tlv->tl);
+	ptr = (u8 *)tlv;
+	ptr += sizeof(*tlv) + tlv_len;
+	tlv = (struct hal_tlv_hdr *)ptr;
+
+	if (FIELD_GET(HAL_TLV_HDR_TAG, tlv->tl) != HAL_RX_PPDU_START)
+		return;
+
+	ptr += sizeof(*tlv);
+	ppdu_start = (struct hal_rx_ppdu_start *)ptr;
+	ppdu_id = FIELD_GET(HAL_RX_PPDU_START_INFO0_PPDU_ID,
+			    __le32_to_cpu(ppdu_start->info0));
+
+	if (pmon->sw_mon_entries.ppdu_id < ppdu_id) {
+		pmon->buf_state = DP_MON_STATUS_LEAD;
+		ppdu_id_diff = ppdu_id - pmon->sw_mon_entries.ppdu_id;
+		if (ppdu_id_diff > ATH11K_DP_RX_FULL_MON_PPDU_ID_WRAP)
+			pmon->buf_state = DP_MON_STATUS_LAG;
+	} else if (pmon->sw_mon_entries.ppdu_id > ppdu_id) {
+		pmon->buf_state = DP_MON_STATUS_LAG;
+		ppdu_id_diff = pmon->sw_mon_entries.ppdu_id - ppdu_id;
+		if (ppdu_id_diff > ATH11K_DP_RX_FULL_MON_PPDU_ID_WRAP)
+			pmon->buf_state = DP_MON_STATUS_LEAD;
+	}
+}
+
 static int ath11k_dp_rx_reap_mon_status_ring(struct ath11k_base *ab, int mac_id,
 					     int *budget, struct sk_buff_head *skb_list)
 {
 	struct ath11k *ar;
 	struct ath11k_pdev_dp *dp;
+	struct ath11k_mon_data *pmon;
 	struct dp_rxdma_ring *rx_ring;
 	struct hal_srng *srng;
 	void *rx_mon_status_desc;
@@ -2882,6 +2920,7 @@  static int ath11k_dp_rx_reap_mon_status_ring(struct ath11k_base *ab, int mac_id,
 
 	ar = ab->pdevs[ath11k_hw_mac_id_to_pdev_id(&ab->hw_params, mac_id)].ar;
 	dp = &ar->dp;
+	pmon = &dp->mon_data;
 	srng_id = ath11k_hw_mac_id_to_srng_id(&ab->hw_params, mac_id);
 	rx_ring = &dp->rx_mon_status_refill_ring[srng_id];
 
@@ -2894,8 +2933,10 @@  static int ath11k_dp_rx_reap_mon_status_ring(struct ath11k_base *ab, int mac_id,
 		*budget -= 1;
 		rx_mon_status_desc =
 			ath11k_hal_srng_src_peek(ab, srng);
-		if (!rx_mon_status_desc)
+		if (!rx_mon_status_desc) {
+			pmon->buf_state = DP_MON_STATUS_REPLINISH;
 			break;
+		}
 
 		ath11k_hal_rx_buf_addr_info_get(rx_mon_status_desc, &paddr,
 						&cookie, &rbm);
@@ -2908,6 +2949,7 @@  static int ath11k_dp_rx_reap_mon_status_ring(struct ath11k_base *ab, int mac_id,
 				ath11k_warn(ab, "rx monitor status with invalid buf_id %d\n",
 					    buf_id);
 				spin_unlock_bh(&rx_ring->idr_lock);
+				pmon->buf_state = DP_MON_STATUS_REPLINISH;
 				goto move_next;
 			}
 
@@ -2927,10 +2969,18 @@  static int ath11k_dp_rx_reap_mon_status_ring(struct ath11k_base *ab, int mac_id,
 					    FIELD_GET(HAL_TLV_HDR_TAG,
 						      tlv->tl));
 				dev_kfree_skb_any(skb);
+				pmon->buf_state = DP_MON_STATUS_NO_DMA;
 				goto move_next;
 			}
 
+			if (ab->hw_params.full_monitor_mode) {
+				ath11k_dp_rx_mon_update_status_buf_state(pmon, tlv);
+				if (paddr == pmon->mon_status_paddr)
+					pmon->buf_state = DP_MON_STATUS_MATCH;
+			}
 			__skb_queue_tail(skb_list, skb);
+		} else {
+			pmon->buf_state = DP_MON_STATUS_REPLINISH;
 		}
 move_next:
 		skb = ath11k_dp_rx_alloc_mon_status_buf(ab, rx_ring,
@@ -5007,6 +5057,360 @@  static void ath11k_dp_rx_mon_status_process_tlv(struct ath11k *ar,
 	}
 }
 
+static u32
+ath11k_dp_rx_full_mon_mpdu_pop(struct ath11k *ar,
+			       void *ring_entry, struct sk_buff **head_msdu,
+			       struct sk_buff **tail_msdu,
+			       struct hal_sw_mon_ring_entries *sw_mon_entries)
+{
+	struct ath11k_pdev_dp *dp = &ar->dp;
+	struct ath11k_mon_data *pmon = &dp->mon_data;
+	struct dp_rxdma_ring *rx_ring = &dp->rxdma_mon_buf_ring;
+	struct sk_buff *msdu = NULL, *last = NULL;
+	struct hal_sw_monitor_ring *sw_desc = ring_entry;
+	struct hal_rx_msdu_list msdu_list;
+	struct hal_rx_desc *rx_desc;
+	struct ath11k_pdev_mon_stats *rx_mon_stats;
+	struct ath11k_skb_rxcb *rxcb;
+	void *rx_msdu_link_desc;
+	void *p_buf_addr_info, *p_last_buf_addr_info;
+	int buf_id, i = 0;
+	u32 rx_buf_size, rx_pkt_offset, l2_hdr_offset;
+	u32 rx_bufs_used = 0, msdu_cnt = 0;
+	u32 total_len = 0, frag_len = 0, sw_cookie;
+	u16 num_msdus = 0;
+	u8 rxdma_err, rbm;
+	bool is_frag, is_first_msdu;
+	bool drop_mpdu = false;
+
+	rx_mon_stats = &pmon->rx_mon_stats;
+
+	ath11k_hal_rx_sw_mon_ring_buf_paddr_get(ring_entry, sw_mon_entries);
+
+	sw_cookie = sw_mon_entries->mon_dst_sw_cookie;
+	sw_mon_entries->end_of_ppdu = false;
+	sw_mon_entries->drop_ppdu = false;
+	p_last_buf_addr_info = sw_mon_entries->dst_buf_addr_info;
+	msdu_cnt = sw_mon_entries->msdu_cnt;
+
+	sw_mon_entries->end_of_ppdu =
+		FIELD_GET(HAL_SW_MON_RING_INFO0_END_OF_PPDU, sw_desc->info0);
+	if (sw_mon_entries->end_of_ppdu)
+		return rx_bufs_used;
+
+	if (FIELD_GET(HAL_SW_MON_RING_INFO0_RXDMA_PUSH_REASON,
+		      sw_desc->info0) ==
+		      HAL_REO_DEST_RING_PUSH_REASON_ERR_DETECTED) {
+		rxdma_err =
+			FIELD_GET(HAL_SW_MON_RING_INFO0_RXDMA_ERROR_CODE,
+				  sw_desc->info0);
+		if (rxdma_err == HAL_REO_ENTR_RING_RXDMA_ECODE_FLUSH_REQUEST_ERR ||
+		    rxdma_err == HAL_REO_ENTR_RING_RXDMA_ECODE_MPDU_LEN_ERR ||
+		    rxdma_err == HAL_REO_ENTR_RING_RXDMA_ECODE_OVERFLOW_ERR) {
+			rx_mon_stats->dest_mpdu_drop++;
+			drop_mpdu = true;
+		}
+	}
+
+	is_frag = false;
+	is_first_msdu = true;
+
+	do {
+		rx_msdu_link_desc =
+			(u8 *)pmon->link_desc_banks[sw_cookie].vaddr +
+			(sw_mon_entries->mon_dst_paddr -
+			 pmon->link_desc_banks[sw_cookie].paddr);
+
+		ath11k_hal_rx_msdu_list_get(ar, rx_msdu_link_desc, &msdu_list,
+					    &num_msdus);
+
+		for (i = 0; i < num_msdus; i++) {
+			buf_id = FIELD_GET(DP_RXDMA_BUF_COOKIE_BUF_ID,
+					   msdu_list.sw_cookie[i]);
+
+			spin_lock_bh(&rx_ring->idr_lock);
+			msdu = idr_find(&rx_ring->bufs_idr, buf_id);
+			if (!msdu) {
+				ath11k_dbg(ar->ab, ATH11K_DBG_DATA,
+					   "full mon msdu_pop: invalid buf_id %d\n",
+					    buf_id);
+				spin_unlock_bh(&rx_ring->idr_lock);
+				break;
+			}
+			idr_remove(&rx_ring->bufs_idr, buf_id);
+			spin_unlock_bh(&rx_ring->idr_lock);
+
+			rxcb = ATH11K_SKB_RXCB(msdu);
+			if (!rxcb->unmapped) {
+				dma_unmap_single(ar->ab->dev, rxcb->paddr,
+						 msdu->len +
+						 skb_tailroom(msdu),
+						 DMA_FROM_DEVICE);
+				rxcb->unmapped = 1;
+			}
+			if (drop_mpdu) {
+				ath11k_dbg(ar->ab, ATH11K_DBG_DATA,
+					   "full mon: i %d drop msdu %p *ppdu_id %x\n",
+					   i, msdu, sw_mon_entries->ppdu_id);
+				dev_kfree_skb_any(msdu);
+				msdu_cnt--;
+				goto next_msdu;
+			}
+
+			rx_desc = (struct hal_rx_desc *)msdu->data;
+
+			rx_pkt_offset = sizeof(struct hal_rx_desc);
+			l2_hdr_offset = ath11k_dp_rx_h_msdu_end_l3pad(ar->ab, rx_desc);
+
+			if (is_first_msdu) {
+				if (!ath11k_dp_rxdesc_mpdu_valid(ar->ab, rx_desc)) {
+					drop_mpdu = true;
+					dev_kfree_skb_any(msdu);
+					msdu = NULL;
+					goto next_msdu;
+				}
+				is_first_msdu = false;
+			}
+
+			ath11k_dp_mon_get_buf_len(&msdu_list.msdu_info[i],
+						  &is_frag, &total_len,
+						  &frag_len, &msdu_cnt);
+
+			rx_buf_size = rx_pkt_offset + l2_hdr_offset + frag_len;
+
+			ath11k_dp_pkt_set_pktlen(msdu, rx_buf_size);
+
+			if (!(*head_msdu))
+				*head_msdu = msdu;
+			else if (last)
+				last->next = msdu;
+
+			last = msdu;
+next_msdu:
+			rx_bufs_used++;
+		}
+
+		ath11k_dp_rx_mon_next_link_desc_get(rx_msdu_link_desc,
+						    &sw_mon_entries->mon_dst_paddr,
+						    &sw_mon_entries->mon_dst_sw_cookie,
+						    &rbm,
+						    &p_buf_addr_info);
+
+		if (ath11k_dp_rx_monitor_link_desc_return(ar,
+							  p_last_buf_addr_info,
+							  dp->mac_id))
+			ath11k_dbg(ar->ab, ATH11K_DBG_DATA,
+				   "full mon: dp_rx_monitor_link_desc_return failed\n");
+
+		p_last_buf_addr_info = p_buf_addr_info;
+
+	} while (sw_mon_entries->mon_dst_paddr && msdu_cnt);
+
+	if (last)
+		last->next = NULL;
+
+	*tail_msdu = msdu;
+
+	return rx_bufs_used;
+}
+
+static int ath11k_dp_rx_full_mon_prepare_mpdu(struct ath11k_dp *dp,
+					      struct dp_full_mon_mpdu *mon_mpdu,
+					      struct sk_buff *head,
+					      struct sk_buff *tail)
+{
+	mon_mpdu = kzalloc(sizeof(*mon_mpdu), GFP_ATOMIC);
+	if (!mon_mpdu)
+		return -ENOMEM;
+
+	list_add_tail(&mon_mpdu->list, &dp->dp_full_mon_mpdu_list);
+	mon_mpdu->head = head;
+	mon_mpdu->tail = tail;
+
+	return 0;
+}
+
+static void ath11k_dp_rx_full_mon_drop_ppdu(struct ath11k_dp *dp,
+					    struct dp_full_mon_mpdu *mon_mpdu)
+{
+	struct dp_full_mon_mpdu *tmp;
+	struct sk_buff *tmp_msdu, *skb_next;
+
+	if (list_empty(&dp->dp_full_mon_mpdu_list))
+		return;
+
+	list_for_each_entry_safe(mon_mpdu, tmp, &dp->dp_full_mon_mpdu_list, list) {
+		list_del(&mon_mpdu->list);
+
+		tmp_msdu = mon_mpdu->head;
+		while (tmp_msdu) {
+			skb_next = tmp_msdu->next;
+			dev_kfree_skb_any(tmp_msdu);
+			tmp_msdu = skb_next;
+		}
+
+		kfree(mon_mpdu);
+	}
+}
+
+static int ath11k_dp_rx_full_mon_deliver_ppdu(struct ath11k *ar,
+					      int mac_id,
+					      struct ath11k_mon_data *pmon,
+					      struct napi_struct *napi)
+{
+	struct ath11k_pdev_mon_stats *rx_mon_stats;
+	struct dp_full_mon_mpdu *tmp;
+	struct dp_full_mon_mpdu *mon_mpdu = pmon->mon_mpdu;
+	struct sk_buff *head_msdu, *tail_msdu;
+	struct ath11k_base *ab = ar->ab;
+	struct ath11k_dp *dp = &ab->dp;
+	int ret;
+
+	rx_mon_stats = &pmon->rx_mon_stats;
+
+	list_for_each_entry_safe(mon_mpdu, tmp, &dp->dp_full_mon_mpdu_list, list) {
+		list_del(&mon_mpdu->list);
+		head_msdu = mon_mpdu->head;
+		tail_msdu = mon_mpdu->tail;
+		if (head_msdu && tail_msdu) {
+			ret = ath11k_dp_rx_mon_deliver(ar, mac_id, head_msdu,
+						       tail_msdu, napi);
+			rx_mon_stats->dest_mpdu_done++;
+			ath11k_dbg(ar->ab, ATH11K_DBG_DATA, "full mon: deliver ppdu\n");
+		}
+		kfree(mon_mpdu);
+	}
+
+	return ret;
+}
+
+static int
+ath11k_dp_rx_process_full_mon_status_ring(struct ath11k_base *ab, int mac_id,
+					  struct napi_struct *napi, int budget)
+{
+	struct ath11k *ar = ab->pdevs[mac_id].ar;
+	struct ath11k_pdev_dp *dp = &ar->dp;
+	struct ath11k_mon_data *pmon = &dp->mon_data;
+	struct hal_sw_mon_ring_entries *sw_mon_entries;
+	int quota = 0, work = 0, count;
+
+	sw_mon_entries = &pmon->sw_mon_entries;
+
+	while (pmon->hold_mon_dst_ring) {
+		quota = ath11k_dp_rx_process_mon_status(ab, mac_id,
+							napi, 1);
+		if (pmon->buf_state == DP_MON_STATUS_MATCH) {
+			count = sw_mon_entries->status_buf_count;
+			if (count > 1) {
+				quota += ath11k_dp_rx_process_mon_status(ab, mac_id,
+									 napi, count);
+			}
+
+			ath11k_dp_rx_full_mon_deliver_ppdu(ar, dp->mac_id,
+							   pmon, napi);
+			pmon->hold_mon_dst_ring = false;
+		} else if (!pmon->mon_status_paddr ||
+			   pmon->buf_state == DP_MON_STATUS_LEAD) {
+			sw_mon_entries->drop_ppdu = true;
+			pmon->hold_mon_dst_ring = false;
+		}
+
+		if (!quota)
+			break;
+
+		work += quota;
+	}
+
+	if (sw_mon_entries->drop_ppdu)
+		ath11k_dp_rx_full_mon_drop_ppdu(&ab->dp, pmon->mon_mpdu);
+
+	return work;
+}
+
+static int ath11k_dp_full_mon_process_rx(struct ath11k_base *ab, int mac_id,
+					 struct napi_struct *napi, int budget)
+{
+	struct ath11k *ar = ab->pdevs[mac_id].ar;
+	struct ath11k_pdev_dp *dp = &ar->dp;
+	struct ath11k_mon_data *pmon = &dp->mon_data;
+	struct hal_sw_mon_ring_entries *sw_mon_entries;
+	struct ath11k_pdev_mon_stats *rx_mon_stats;
+	struct sk_buff *head_msdu, *tail_msdu;
+	void *mon_dst_srng = &ar->ab->hal.srng_list[dp->rxdma_mon_dst_ring.ring_id];
+	void *ring_entry;
+	u32 rx_bufs_used = 0, mpdu_rx_bufs_used;
+	int quota = 0, ret;
+	bool break_dst_ring = false;
+
+	spin_lock_bh(&pmon->mon_lock);
+
+	sw_mon_entries = &pmon->sw_mon_entries;
+	rx_mon_stats = &pmon->rx_mon_stats;
+
+	if (pmon->hold_mon_dst_ring) {
+		spin_unlock_bh(&pmon->mon_lock);
+		goto reap_status_ring;
+	}
+
+	ath11k_hal_srng_access_begin(ar->ab, mon_dst_srng);
+	while ((ring_entry = ath11k_hal_srng_dst_peek(ar->ab, mon_dst_srng))) {
+		head_msdu = NULL;
+		tail_msdu = NULL;
+
+		mpdu_rx_bufs_used = ath11k_dp_rx_full_mon_mpdu_pop(ar, ring_entry,
+								   &head_msdu,
+								   &tail_msdu,
+								   sw_mon_entries);
+		rx_bufs_used += mpdu_rx_bufs_used;
+
+		if (!sw_mon_entries->end_of_ppdu) {
+			if (head_msdu) {
+				ret = ath11k_dp_rx_full_mon_prepare_mpdu(&ab->dp,
+									 pmon->mon_mpdu,
+									 head_msdu,
+									 tail_msdu);
+				if (ret)
+					break_dst_ring = true;
+			}
+
+			goto next_entry;
+		} else {
+			if (!sw_mon_entries->ppdu_id &&
+			    !sw_mon_entries->mon_status_paddr) {
+				break_dst_ring = true;
+				goto next_entry;
+			}
+		}
+
+		rx_mon_stats->dest_ppdu_done++;
+		pmon->mon_ppdu_status = DP_PPDU_STATUS_START;
+		pmon->buf_state = DP_MON_STATUS_LAG;
+		pmon->mon_status_paddr = sw_mon_entries->mon_status_paddr;
+		pmon->hold_mon_dst_ring = true;
+next_entry:
+		ring_entry = ath11k_hal_srng_dst_get_next_entry(ar->ab,
+								mon_dst_srng);
+		if (break_dst_ring)
+			break;
+	}
+
+	ath11k_hal_srng_access_end(ar->ab, mon_dst_srng);
+	spin_unlock_bh(&pmon->mon_lock);
+
+	if (rx_bufs_used) {
+		ath11k_dp_rxbufs_replenish(ar->ab, dp->mac_id,
+					   &dp->rxdma_mon_buf_ring,
+					   rx_bufs_used,
+					   HAL_RX_BUF_RBM_SW3_BM);
+	}
+
+reap_status_ring:
+	quota = ath11k_dp_rx_process_full_mon_status_ring(ab, mac_id,
+							  napi, budget);
+
+	return quota;
+}
+
 static int ath11k_dp_mon_process_rx(struct ath11k_base *ab, int mac_id,
 				    struct napi_struct *napi, int budget)
 {
@@ -5029,10 +5433,13 @@  int ath11k_dp_rx_process_mon_rings(struct ath11k_base *ab, int mac_id,
 	struct ath11k *ar = ath11k_ab_to_ar(ab, mac_id);
 	int ret = 0;
 
-	if (ar->monitor_started)
+	if (!ar->monitor_started)
+		ret = ath11k_dp_rx_process_mon_status(ab, mac_id, napi, budget);
+	else if (!ab->hw_params.full_monitor_mode)
 		ret = ath11k_dp_mon_process_rx(ab, mac_id, napi, budget);
 	else
-		ret = ath11k_dp_rx_process_mon_status(ab, mac_id, napi, budget);
+		ret = ath11k_dp_full_mon_process_rx(ab, mac_id, napi, budget);
+
 	return ret;
 }