Prechádzať zdrojové kódy

qcacld-3.0: LRO - Fix LRO disable crash

- Fix a crash seen due to memory being freed twice
- Deregister the LRO flush callback during LRO disable

CRs-Fixed: 933123
Change-Id: I18edadc620da04b9a571da6e1b143c099c308d90
Dhanashri Atre 9 rokov pred
rodič
commit
8d97817435

+ 1 - 0
core/dp/ol/inc/ol_txrx_osif_api.h

@@ -279,5 +279,6 @@ void ol_txrx_flush_rx_frames(struct ol_txrx_peer_t *peer,
 
 #if defined(FEATURE_LRO)
 void ol_register_lro_flush_cb(void (handler)(void *), void *data);
+void ol_deregister_lro_flush_cb(void);
 #endif
 #endif /* _OL_TXRX_OSIF_API__H_ */

+ 54 - 1
core/dp/txrx/ol_txrx.c

@@ -3112,6 +3112,18 @@ CDF_STATUS ol_txrx_register_pause_cb(ol_tx_pause_callback_fp pause_cb)
 #endif
 
 #if defined(FEATURE_LRO)
+/**
+ * ol_txrx_lro_flush_handler() - LRO flush handler
+ * @context: dev handle
+ * @rxpkt: rx data
+ * @staid: station id
+ *
+ * This function handles an LRO flush indication.
+ * If the rx thread is enabled, it will be invoked by the rx
+ * thread else it will be called in the tasklet context
+ *
+ * Return: none
+ */
 void ol_txrx_lro_flush_handler(void *context,
 	 void *rxpkt,
 	 uint16_t staid)
@@ -3132,6 +3144,15 @@ void ol_txrx_lro_flush_handler(void *context,
 			 "%s: lro_flush_cb NULL", __func__);
 }
 
+/**
+ * ol_txrx_lro_flush() - LRO flush callback
+ * @data: opaque data pointer
+ *
+ * This is the callback registered with CE to trigger
+ * an LRO flush
+ *
+ * Return: none
+ */
 void ol_txrx_lro_flush(void *data)
 {
 	p_cds_sched_context sched_ctx = get_cds_sched_ctxt();
@@ -3151,7 +3172,8 @@ void ol_txrx_lro_flush(void *data)
 			return;
 		}
 
-		pkt->callback = (cds_ol_rx_thread_cb) ol_txrx_lro_flush_handler;
+		pkt->callback =
+			 (cds_ol_rx_thread_cb) ol_txrx_lro_flush_handler;
 		pkt->context = pdev;
 		pkt->Rxpkt = NULL;
 		pkt->staId = 0;
@@ -3159,6 +3181,16 @@ void ol_txrx_lro_flush(void *data)
 	}
 }
 
+/**
+ * ol_register_lro_flush_cb() - register the LRO flush callback
+ * @handler: callback function
+ * @data: opaque data pointer to be passed back
+ *
+ * Store the LRO flush callback provided and in turn
+ * register OL's LRO flush handler with CE
+ *
+ * Return: none
+ */
 void ol_register_lro_flush_cb(void (handler)(void *), void *data)
 {
 	struct ol_softc *hif_device =
@@ -3170,4 +3202,25 @@ void ol_register_lro_flush_cb(void (handler)(void *), void *data)
 
 	ce_lro_flush_cb_register(hif_device, ol_txrx_lro_flush, pdev);
 }
+
+/**
+ * ol_deregister_lro_flush_cb() - deregister the LRO flush
+ * callback
+ *
+ * Remove the LRO flush callback provided and in turn
+ * deregister OL's LRO flush handler with CE
+ *
+ * Return: none
+ */
+void ol_deregister_lro_flush_cb(void)
+{
+	struct ol_softc *hif_device =
+		(struct ol_softc *)cds_get_context(CDF_MODULE_ID_HIF);
+	struct ol_txrx_pdev_t *pdev = cds_get_context(CDF_MODULE_ID_TXRX);
+
+	ce_lro_flush_cb_deregister(hif_device);
+
+	pdev->lro_info.lro_flush_cb = NULL;
+	pdev->lro_info.lro_data = NULL;
+}
 #endif /* FEATURE_LRO */

+ 12 - 19
core/hdd/src/wlan_hdd_lro.c

@@ -138,14 +138,7 @@ static void hdd_lro_desc_info_init(struct hdd_lro_s *hdd_info)
  */
 static void hdd_lro_desc_pool_deinit(struct hdd_lro_desc_pool *lro_desc_pool)
 {
-
-	if (lro_desc_pool->lro_desc_array) {
-		cdf_mem_free(lro_desc_pool->lro_desc_array);
-		lro_desc_pool->lro_desc_array = NULL;
-	}
-
 	INIT_LIST_HEAD(&lro_desc_pool->lro_free_list_head);
-
 	cdf_spinlock_destroy(&lro_desc_pool->lro_pool_lock);
 }
 
@@ -161,19 +154,10 @@ static void hdd_lro_desc_pool_deinit(struct hdd_lro_desc_pool *lro_desc_pool)
  */
 static void hdd_lro_desc_info_deinit(struct hdd_lro_s *hdd_info)
 {
-	int i;
 	struct hdd_lro_desc_info *desc_info = &hdd_info->lro_desc_info;
 
-	cdf_mem_free(hdd_info->lro_mgr->lro_arr);
-	hdd_info->lro_mgr->lro_arr = NULL;
 	hdd_lro_desc_pool_deinit(&desc_info->lro_desc_pool);
-	/* Free the a list of LRO desc for each entry of the hash table */
-	for (i = 0; i < LRO_DESC_TABLE_SZ; i++)
-		INIT_LIST_HEAD(&desc_info->lro_hash_table[i].lro_desc_list);
-
 	cdf_spinlock_destroy(&desc_info->lro_hash_lock);
-	cdf_mem_free(desc_info->lro_hash_table);
-	desc_info->lro_hash_table = NULL;
 }
 
 /**
@@ -605,9 +589,18 @@ void hdd_lro_disable(hdd_context_t *hdd_ctx, hdd_adapter_t *adapter)
 		 NL80211_IFTYPE_STATION != adapter->wdev.iftype)
 		return;
 
-	hdd_lro_desc_info_deinit(&adapter->lro_info);
-	cdf_mem_free(adapter->lro_info.lro_mgr);
-	adapter->lro_info.lro_mgr = NULL;
+	/* Deregister the flush callback */
+	ol_deregister_lro_flush_cb();
+
+	if (adapter->lro_info.lro_mgr) {
+		hdd_lro_desc_info_deinit(&adapter->lro_info);
+		cdf_mem_free(adapter->lro_info.lro_mgr);
+		adapter->lro_info.lro_mgr = NULL;
+		adapter->lro_info.lro_desc_info.
+			lro_desc_pool.lro_desc_array = NULL;
+		adapter->lro_info.lro_desc_info.
+			lro_hash_table = NULL;
+	}
 	return;
 }
 

+ 1 - 0
core/hif/src/ce/ce_api.h

@@ -467,5 +467,6 @@ bool ce_check_rx_pending(struct ol_softc *scn, int ce_id);
 #if defined(FEATURE_LRO)
 void ce_lro_flush_cb_register(struct ol_softc *scn,
 	 void (handler)(void *), void *data);
+void ce_lro_flush_cb_deregister(struct ol_softc *scn);
 #endif
 #endif /* __COPY_ENGINE_API_H__ */

+ 58 - 3
core/hif/src/ce/ce_main.c

@@ -2584,10 +2584,65 @@ u32 shadow_dst_wr_ind_addr(struct ol_softc *scn, u32 ctrl_addr)
 }
 #endif
 
+#if defined(FEATURE_LRO)
+/**
+ * ce_lro_flush_cb_register() - register the LRO flush
+ * callback
+ * @scn: HIF context
+ * @handler: callback function
+ * @data: opaque data pointer to be passed back
+ *
+ * Store the LRO flush callback provided
+ *
+ * Return: none
+ */
 void ce_lro_flush_cb_register(struct ol_softc *scn,
 	 void (handler)(void *), void *data)
 {
-	struct CE_state *ce_state = scn->ce_id_to_state[CE_HTT_T2H_MSG];
-	ce_state->lro_flush_cb = handler;
-	ce_state->lro_data = data;
+	uint8_t ul, dl;
+	int ul_polled, dl_polled;
+
+	CDF_ASSERT(scn != NULL);
+
+	if (CDF_STATUS_SUCCESS !=
+		 hif_map_service_to_pipe(scn, HTT_DATA_MSG_SVC,
+			 &ul, &dl, &ul_polled, &dl_polled)) {
+		printk("%s cannot map service to pipe\n", __FUNCTION__);
+		return;
+	} else {
+		struct CE_state *ce_state;
+		ce_state = scn->ce_id_to_state[dl];
+		ce_state->lro_flush_cb = handler;
+		ce_state->lro_data = data;
+	}
+}
+
+/**
+ * ce_lro_flush_cb_deregister() - deregister the LRO flush
+ * callback
+ * @scn: HIF context
+ *
+ * Remove the LRO flush callback
+ *
+ * Return: none
+ */
+void ce_lro_flush_cb_deregister(struct ol_softc *scn)
+{
+	uint8_t ul, dl;
+	int ul_polled, dl_polled;
+
+	CDF_ASSERT(scn != NULL);
+
+	if (CDF_STATUS_SUCCESS !=
+		 hif_map_service_to_pipe(scn, HTT_DATA_MSG_SVC,
+			 &ul, &dl, &ul_polled, &dl_polled)) {
+		printk("%s cannot map service to pipe\n", __FUNCTION__);
+		return;
+	} else {
+		struct CE_state *ce_state;
+		ce_state = scn->ce_id_to_state[dl];
+		ce_state->lro_flush_cb = NULL;
+		ce_state->lro_data = NULL;
+	}
 }
+#endif

+ 1 - 2
core/hif/src/ce/ce_tasklet.c

@@ -214,8 +214,7 @@ static void ce_tasklet(unsigned long data)
 
 	ce_per_engine_service(scn, tasklet_entry->ce_id);
 
-	if (tasklet_entry->ce_id == CE_HTT_T2H_MSG &&
-			 CE_state->lro_flush_cb != NULL) {
+	if (CE_state->lro_flush_cb != NULL) {
 		CE_state->lro_flush_cb(CE_state->lro_data);
 	}
 

+ 8 - 0
core/hif/src/hif_napi.c

@@ -38,6 +38,7 @@
 #include <hif_debug.h>
 #include <hif_io32.h>
 #include <ce_api.h>
+#include <ce_internal.h>
 
 enum napi_decision_vector {
 	HIF_NAPI_NOEVENT = 0,
@@ -399,6 +400,7 @@ int hif_napi_poll(struct napi_struct *napi, int budget)
 	int    cpu = smp_processor_id();
 	struct ol_softc      *hif;
 	struct qca_napi_info *napi_info;
+	struct CE_state *ce_state;
 
 	NAPI_DEBUG("%s -->(.., budget=%d)\n", budget);
 
@@ -413,6 +415,12 @@ int hif_napi_poll(struct napi_struct *napi, int budget)
 		__func__, rc);
 	napi_info->stats[cpu].napi_workdone += rc;
 	normalized = (rc / napi_info->scale);
+
+	ce_state = hif->ce_id_to_state[NAPI_ID2PIPE(napi_info->id)];
+	if (ce_state->lro_flush_cb != NULL) {
+		ce_state->lro_flush_cb(ce_state->lro_data);
+	}
+
 	/* do not return 0, if there was some work done,
 	   even if it is below the scale   */
 	if (rc)