From 7f73d6d12dcf66c49af52c8f967cfa3d4d1f7117 Mon Sep 17 00:00:00 2001 From: "Poddar, Siddarth" Date: Mon, 9 Jan 2017 16:45:39 +0530 Subject: qcacld-3.0: Register Bus bandwidth timer directly with kernel Currently, bus bw timer is defined globally and it access the adapter nodes.The crash can happen when the adapter is getting deregistered in one thread and in another thread bus bw timer handler is trying to access the same adapter. To mitigate this issue, bus bw timer needs to directly registered with kernel and once the timer callback is executed then it needs to schedule a workqueue where all the processing is done. While closing the adapter, do flush_work for this timer to make sure that workqueue is done and then it is closing the adapter. CRs-Fixed: 1104615 Change-Id: I81a7bfa43e3fb43488966dda702c6f394f9b40c7 --- core/hdd/inc/wlan_hdd_main.h | 4 ++- core/hdd/src/wlan_hdd_main.c | 69 ++++++++++++++++++++++++++++++++++---------- 2 files changed, 56 insertions(+), 17 deletions(-) diff --git a/core/hdd/inc/wlan_hdd_main.h b/core/hdd/inc/wlan_hdd_main.h index 2387be03a9fc..3a2756da823d 100644 --- a/core/hdd/inc/wlan_hdd_main.h +++ b/core/hdd/inc/wlan_hdd_main.h @@ -1434,7 +1434,9 @@ struct hdd_context_s { #ifdef MSM_PLATFORM /* DDR bus bandwidth compute timer */ - qdf_mc_timer_t bus_bw_timer; + qdf_timer_t bus_bw_timer; + bool bus_bw_timer_started; + struct work_struct bus_bw_work; int cur_vote_level; spinlock_t bus_bw_lock; int cur_rx_level; diff --git a/core/hdd/src/wlan_hdd_main.c b/core/hdd/src/wlan_hdd_main.c index 5a4b7595faa0..a53035f84264 100644 --- a/core/hdd/src/wlan_hdd_main.c +++ b/core/hdd/src/wlan_hdd_main.c @@ -3384,6 +3384,8 @@ QDF_STATUS hdd_close_adapter(hdd_context_t *hdd_ctx, hdd_adapter_t *adapter, } adapterNode = pCurrent; if (QDF_STATUS_SUCCESS == status) { + hdd_info("wait for bus bw work to flush"); + cancel_work_sync(&hdd_ctx->bus_bw_work); cds_clear_concurrency_mode(adapter->device_mode); hdd_cleanup_adapter(hdd_ctx, adapterNode->pAdapter, rtnl_held); @@ -5401,9 +5403,10 @@ static void hdd_pld_request_bus_bandwidth(hdd_context_t *hdd_ctx, } #define HDD_BW_GET_DIFF(_x, _y) (unsigned long)((ULONG_MAX - (_y)) + (_x) + 1) -static void hdd_bus_bw_compute_cbk(void *priv) +static void hdd_bus_bw_work_handler(struct work_struct *work) { - hdd_context_t *hdd_ctx = (hdd_context_t *) priv; + hdd_context_t *hdd_ctx = container_of(work, hdd_context_t, + bus_bw_work); hdd_adapter_t *adapter = NULL; uint64_t tx_packets = 0, rx_packets = 0; uint64_t fwd_tx_packets = 0, fwd_rx_packets = 0; @@ -5500,28 +5503,62 @@ static void hdd_bus_bw_compute_cbk(void *priv) hdd_ipa_set_perf_level(hdd_ctx, tx_packets, rx_packets); hdd_ipa_uc_stat_request(adapter, 2); - qdf_mc_timer_start(&hdd_ctx->bus_bw_timer, + qdf_timer_start(&hdd_ctx->bus_bw_timer, hdd_ctx->config->busBandwidthComputeInterval); } +/** + * __hdd_bus_bw_cbk() - Bus bandwidth data structure callback. + * @arg: Argument of timer function + * + * Schedule a workqueue in this function where all the processing is done. + * + * Return: None. + */ +static void __hdd_bus_bw_cbk(void *arg) +{ + hdd_context_t *hdd_ctx = (hdd_context_t *) arg; + + if (wlan_hdd_validate_context(hdd_ctx)) + return; + + schedule_work(&hdd_ctx->bus_bw_work); +} + +/** + * hdd_bus_bw_cbk() - Wrapper for bus bw callback for SSR protection. + * @arg: Argument of timer function + * + * Return: None. + */ +static void hdd_bus_bw_cbk(void *arg) +{ + cds_ssr_protect(__func__); + __hdd_bus_bw_cbk(arg); + cds_ssr_unprotect(__func__); +} + int hdd_bus_bandwidth_init(hdd_context_t *hdd_ctx) { spin_lock_init(&hdd_ctx->bus_bw_lock); - - qdf_mc_timer_init(&hdd_ctx->bus_bw_timer, - QDF_TIMER_TYPE_SW, - hdd_bus_bw_compute_cbk, (void *)hdd_ctx); + INIT_WORK(&hdd_ctx->bus_bw_work, + hdd_bus_bw_work_handler); + qdf_timer_init(NULL, + &hdd_ctx->bus_bw_timer, + hdd_bus_bw_cbk, (void *)hdd_ctx, + QDF_TIMER_TYPE_SW); return 0; } void hdd_bus_bandwidth_destroy(hdd_context_t *hdd_ctx) { - if (qdf_mc_timer_get_current_state(&hdd_ctx->bus_bw_timer) == - QDF_TIMER_STATE_RUNNING) + if (hdd_ctx->bus_bw_timer_started) hdd_reset_tcp_delack(hdd_ctx); - qdf_mc_timer_destroy(&hdd_ctx->bus_bw_timer); + hdd_info("wait for bus bw work to flush"); + cancel_work_sync(&hdd_ctx->bus_bw_work); + qdf_timer_free(&hdd_ctx->bus_bw_timer); } #endif @@ -8881,11 +8918,11 @@ void hdd_start_bus_bw_compute_timer(hdd_adapter_t *adapter) { hdd_context_t *hdd_ctx = WLAN_HDD_GET_CTX(adapter); - if (QDF_TIMER_STATE_RUNNING == - qdf_mc_timer_get_current_state(&hdd_ctx->bus_bw_timer)) + if (hdd_ctx->bus_bw_timer_started) return; - qdf_mc_timer_start(&hdd_ctx->bus_bw_timer, + hdd_ctx->bus_bw_timer_started = true; + qdf_timer_start(&hdd_ctx->bus_bw_timer, hdd_ctx->config->busBandwidthComputeInterval); } @@ -8896,8 +8933,7 @@ void hdd_stop_bus_bw_compute_timer(hdd_adapter_t *adapter) bool can_stop = true; hdd_context_t *hdd_ctx = WLAN_HDD_GET_CTX(adapter); - if (QDF_TIMER_STATE_RUNNING != - qdf_mc_timer_get_current_state(&hdd_ctx->bus_bw_timer)) { + if (!hdd_ctx->bus_bw_timer_started) { /* trying to stop timer, when not running is not good */ hdd_info("bus band width compute timer is not running"); return; @@ -8935,7 +8971,8 @@ void hdd_stop_bus_bw_compute_timer(hdd_adapter_t *adapter) if (can_stop == true) { /* reset the ipa perf level */ hdd_ipa_set_perf_level(hdd_ctx, 0, 0); - qdf_mc_timer_stop(&hdd_ctx->bus_bw_timer); + qdf_timer_stop(&hdd_ctx->bus_bw_timer); + hdd_ctx->bus_bw_timer_started = false; hdd_reset_tcp_delack(hdd_ctx); } } -- cgit v1.2.3