qcacmn: Add support to flush rx packets for a vdev

When a particular vdev is deleted, the corresponding rx
packets which have been queued to the rx thread are not
flushed. Hence when such packets are submitted to the
network stack, the dev for this skb will be invalid,
since we have already freed the adapter.

Flush out the packets in the rx thread queues, before
deleting the vdev.

CRs-Fixed: 2543392
Change-Id: I2490d0f5ce965f62152613a17a59232521ca058f
diff --git a/dp/inc/cdp_txrx_cmn_struct.h b/dp/inc/cdp_txrx_cmn_struct.h
index 885b2a4..2b6ec91 100644
--- a/dp/inc/cdp_txrx_cmn_struct.h
+++ b/dp/inc/cdp_txrx_cmn_struct.h
@@ -672,6 +672,14 @@
 typedef QDF_STATUS(*ol_txrx_rx_fp)(void *osif_dev, qdf_nbuf_t msdu_list);
 
 /**
+ * ol_txrx_rx_flush_fp - receive function to hand batches of data
+ * frames from txrx to OS shim
+ * @osif_dev: handle to the OSIF virtual device object
+ * @vdev_id: vdev_if of the packets to be flushed
+ */
+typedef QDF_STATUS(*ol_txrx_rx_flush_fp)(void *osif_dev, uint8_t vdev_id);
+
+/**
  * ol_txrx_rx_gro_flush_ind - function to send GRO flush indication to stack
  * for a given RX Context Id.
  * @osif_dev - handle to the OSIF virtual device object
@@ -832,6 +840,7 @@
 	struct {
 		ol_txrx_rx_fp           rx;
 		ol_txrx_rx_fp           rx_stack;
+		ol_txrx_rx_flush_fp     rx_flush;
 		ol_txrx_rx_gro_flush_ind_fp           rx_gro_flush;
 		ol_txrx_rx_check_wai_fp wai_check;
 		ol_txrx_rx_mon_fp       mon;
diff --git a/dp/wifi3.0/dp_main.c b/dp/wifi3.0/dp_main.c
index 12a2984..9f8ad32 100644
--- a/dp/wifi3.0/dp_main.c
+++ b/dp/wifi3.0/dp_main.c
@@ -4869,6 +4869,7 @@
 	vdev->ctrl_vdev = ctrl_vdev;
 	vdev->osif_rx = txrx_ops->rx.rx;
 	vdev->osif_rx_stack = txrx_ops->rx.rx_stack;
+	vdev->osif_rx_flush = txrx_ops->rx.rx_flush;
 	vdev->osif_gro_flush = txrx_ops->rx.rx_gro_flush;
 	vdev->osif_rsim_rx_decap = txrx_ops->rx.rsim_rx_decap;
 	vdev->osif_get_key = txrx_ops->get_key;
@@ -5085,6 +5086,7 @@
 
 	qdf_spin_lock_bh(&pdev->vdev_list_lock);
 	dp_tx_vdev_detach(vdev);
+	dp_rx_vdev_detach(vdev);
 	/* remove the vdev from its parent pdev's list */
 	TAILQ_REMOVE(&pdev->vdev_list, vdev, vdev_list_elem);
 	QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_INFO_HIGH,
diff --git a/dp/wifi3.0/dp_rx.c b/dp/wifi3.0/dp_rx.c
index 049447d..4c9dd0f 100644
--- a/dp/wifi3.0/dp_rx.c
+++ b/dp/wifi3.0/dp_rx.c
@@ -2268,8 +2268,24 @@
 	return rx_bufs_used; /* Assume no scale factor for now */
 }
 
+QDF_STATUS dp_rx_vdev_detach(struct dp_vdev *vdev)
+{
+	QDF_STATUS ret;
+
+	if (vdev->osif_rx_flush) {
+		ret = vdev->osif_rx_flush(vdev->osif_vdev, vdev->vdev_id);
+		if (!ret) {
+			dp_err("Failed to flush rx pkts for vdev %d\n",
+			       vdev->vdev_id);
+			return ret;
+		}
+	}
+
+	return QDF_STATUS_SUCCESS;
+}
+
 /**
- * dp_rx_detach() - detach dp rx
+ * dp_rx_pdev_detach() - detach dp rx
  * @pdev: core txrx pdev context
  *
  * This function will detach DP RX into main device context
diff --git a/dp/wifi3.0/dp_rx.h b/dp/wifi3.0/dp_rx.h
index 0303695..3940f34 100644
--- a/dp/wifi3.0/dp_rx.h
+++ b/dp/wifi3.0/dp_rx.h
@@ -445,6 +445,15 @@
 
 void dp_print_napi_stats(struct dp_soc *soc);
 
+/**
+ * dp_rx_vdev_detach() - detach vdev from dp rx
+ * @vdev: virtual device instance
+ *
+ * Return: QDF_STATUS_SUCCESS: success
+ *         QDF_STATUS_E_RESOURCES: Error return
+ */
+QDF_STATUS dp_rx_vdev_detach(struct dp_vdev *vdev);
+
 uint32_t
 dp_rx_process(struct dp_intr *int_ctx, hal_ring_handle_t hal_ring_hdl,
 	      uint8_t reo_ring_num,
diff --git a/dp/wifi3.0/dp_types.h b/dp/wifi3.0/dp_types.h
index 25400bb..b0c7aab 100644
--- a/dp/wifi3.0/dp_types.h
+++ b/dp/wifi3.0/dp_types.h
@@ -1738,6 +1738,8 @@
 	ol_txrx_rx_fp osif_rx;
 	/* callback to deliver rx frames to the OS */
 	ol_txrx_rx_fp osif_rx_stack;
+	/* call back function to flush out queued rx packets*/
+	ol_txrx_rx_flush_fp osif_rx_flush;
 	ol_txrx_rsim_rx_decap_fp osif_rsim_rx_decap;
 	ol_txrx_get_key_fp osif_get_key;
 	ol_txrx_tx_free_ext_fp osif_tx_free_ext;
diff --git a/qdf/inc/qdf_nbuf.h b/qdf/inc/qdf_nbuf.h
index 154d595..b19d0b1 100644
--- a/qdf/inc/qdf_nbuf.h
+++ b/qdf/inc/qdf_nbuf.h
@@ -757,6 +757,9 @@
 	__qdf_nbuf_set_send_complete_flag(buf, flag);
 }
 
+#define QDF_NBUF_QUEUE_WALK_SAFE(queue, var, tvar)	\
+		__qdf_nbuf_queue_walk_safe(queue, var, tvar)
+
 #ifdef NBUF_MAP_UNMAP_DEBUG
 /**
  * qdf_nbuf_map_check_for_leaks() - check for nbut map leaks
@@ -964,6 +967,28 @@
 	return __qdf_nbuf_queue_head_purge(nbuf_queue_head);
 }
 
+/**
+ * qdf_nbuf_queue_head_lock() - Acquire the nbuf_queue_head lock
+ * @head: nbuf_queue_head of the nbuf_list for which lock is to be acquired
+ *
+ * Return: void
+ */
+static inline void qdf_nbuf_queue_head_lock(qdf_nbuf_queue_head_t *head)
+{
+	__qdf_nbuf_queue_head_lock(head);
+}
+
+/**
+ * qdf_nbuf_queue_head_unlock() - Release the nbuf queue lock
+ * @head: nbuf_queue_head of the nbuf_list for which lock is to be release
+ *
+ * Return: void
+ */
+static inline void qdf_nbuf_queue_head_unlock(qdf_nbuf_queue_head_t *head)
+{
+	__qdf_nbuf_queue_head_unlock(head);
+}
+
 static inline void
 qdf_nbuf_sync_for_cpu(qdf_device_t osdev, qdf_nbuf_t buf, qdf_dma_dir_t dir)
 {
@@ -1867,6 +1892,22 @@
 }
 
 /**
+ * qdf_nbuf_unlink_no_lock() - unlink a nbuf from nbuf list
+ * @buf: Network buf instance
+ * @list: list to use
+ *
+ * This is a lockless version, driver must acquire locks if it
+ * needs to synchronize
+ *
+ * Return: none
+ */
+static inline void
+qdf_nbuf_unlink_no_lock(qdf_nbuf_t buf, qdf_nbuf_queue_head_t *list)
+{
+	__qdf_nbuf_unlink_no_lock(buf, list);
+}
+
+/**
  * qdf_nbuf_reset() - reset the buffer data and pointer
  * @buf: Network buf instance
  * @reserve: reserve
diff --git a/qdf/linux/src/i_qdf_nbuf.h b/qdf/linux/src/i_qdf_nbuf.h
index 9dae8a7..6b50cb1 100644
--- a/qdf/linux/src/i_qdf_nbuf.h
+++ b/qdf/linux/src/i_qdf_nbuf.h
@@ -705,6 +705,9 @@
 #define __qdf_nbuf_data_attr_set(skb, data_attr) \
 	(QDF_NBUF_CB_TX_DATA_ATTR(skb) = (data_attr))
 
+#define __qdf_nbuf_queue_walk_safe(queue, var, tvar)	\
+		skb_queue_walk_safe(queue, var, tvar)
+
 /**
  * __qdf_nbuf_num_frags_init() - init extra frags
  * @skb: sk buffer
@@ -1086,6 +1089,22 @@
 }
 
 /**
+ * __qdf_nbuf_unlink_no_lock() - unlink an skb from skb queue
+ * @skb: Pointer to network buffer
+ * @list: list to use
+ *
+ * This is a lockless version, driver must acquire locks if it
+ * needs to synchronize
+ *
+ * Return: none
+ */
+static inline void
+__qdf_nbuf_unlink_no_lock(struct sk_buff *skb, struct sk_buff_head *list)
+{
+	__skb_unlink(skb, list);
+}
+
+/**
  * __qdf_nbuf_reset() - reset the buffer data and pointer
  * @buf: Network buf instance
  * @reserve: reserve
@@ -2204,6 +2223,30 @@
 	return skb_queue_purge(skb_queue_head);
 }
 
+/**
+ * __qdf_nbuf_queue_head_lock() - Acquire the skb list lock
+ * @head: skb list for which lock is to be acquired
+ *
+ * Return: void
+ */
+static inline
+void __qdf_nbuf_queue_head_lock(struct sk_buff_head *skb_queue_head)
+{
+	spin_lock_bh(&skb_queue_head->lock);
+}
+
+/**
+ * __qdf_nbuf_queue_head_unlock() - Release the skb list lock
+ * @head: skb list for which lock is to be release
+ *
+ * Return: void
+ */
+static inline
+void __qdf_nbuf_queue_head_unlock(struct sk_buff_head *skb_queue_head)
+{
+	spin_unlock_bh(&skb_queue_head->lock);
+}
+
 #ifdef CONFIG_NBUF_AP_PLATFORM
 #include <i_qdf_nbuf_w.h>
 #else