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
This commit is contained in:
Dhanashri Atre 2015-10-30 15:12:03 -07:00 • committed by Prakash Dhavali
commit 8d97817435
7 changed files with 135 additions and 25 deletions

View file

@ -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_ */

View file

@ -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 */

View file

@ -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;
}

View file

@ -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__ */

View file

@ -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

View file

@ -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);
}

View file

@ -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)