qcacld-3.0: Stop bandwidth timer before adapter cleanup

There is race condition between the bus bandwidth work and cleaning up
an adapter. Under some conditions, it is possible for the bus bandwidth
work to access a paritally destroyed adapter, leading to a
use-after-free. To prevent the race condition, use the following
sequence:
    1) Stop the bandwidth timer
    2) Flush pending bandwidth work
    3) Cleanup the adapter
    4) Restart the bandwidth timer, if needed

Change-Id: I7166e75e65433d2dcb818ff8b41fe959c510a2e9
CRs-Fixed: 2025184
This commit is contained in:
Dustin Brown
2017-03-31 12:11:40 -07:00
committed by Sandeep Puligilla
parent 14a8e0312e
commit 5ec6b5598e
4 changed files with 176 additions and 68 deletions

View File

@@ -1762,8 +1762,43 @@ hdd_wlan_get_ibss_mac_addr_from_staid(hdd_adapter_t *pAdapter,
uint8_t staIdx);
void hdd_checkandupdate_phymode(hdd_context_t *pHddCtx);
#ifdef MSM_PLATFORM
void hdd_start_bus_bw_compute_timer(hdd_adapter_t *pAdapter);
void hdd_stop_bus_bw_compute_timer(hdd_adapter_t *pAdapter);
/**
* hdd_bus_bw_compute_timer_start() - start the bandwidth timer
* @hdd_ctx: the global hdd context
*
* Return: None
*/
void hdd_bus_bw_compute_timer_start(hdd_context_t *hdd_ctx);
/**
* hdd_bus_bw_compute_timer_try_start() - try to start the bandwidth timer
* @hdd_ctx: the global hdd context
*
* This function ensures there is at least one adapter in the associated state
* before starting the bandwidth timer.
*
* Return: None
*/
void hdd_bus_bw_compute_timer_try_start(hdd_context_t *hdd_ctx);
/**
* hdd_bus_bw_compute_timer_stop() - stop the bandwidth timer
* @hdd_ctx: the global hdd context
*
* Return: None
*/
void hdd_bus_bw_compute_timer_stop(hdd_context_t *hdd_ctx);
/**
* hdd_bus_bw_compute_timer_try_stop() - try to stop the bandwidth timer
* @hdd_ctx: the global hdd context
*
* This function ensures there are no adapters in the associated state before
* stopping the bandwidth timer.
*
* Return: None
*/
void hdd_bus_bw_compute_timer_try_stop(hdd_context_t *hdd_ctx);
/**
* hdd_bus_bandwidth_init() - Initialize bus bandwidth data structures.
@@ -1785,14 +1820,25 @@ int hdd_bus_bandwidth_init(hdd_context_t *hdd_ctx);
*/
void hdd_bus_bandwidth_destroy(hdd_context_t *hdd_ctx);
#else
static inline void hdd_start_bus_bw_compute_timer(hdd_adapter_t *pAdapter)
static inline void hdd_bus_bw_compute_timer_start(hdd_context_t *hdd_ctx)
{
}
static inline void hdd_bus_bw_compute_timer_try_start(hdd_context_t *hdd_ctx)
{
}
static inline void hdd_bus_bw_compute_timer_stop(hdd_context_t *hdd_ctx)
{
}
static inline void hdd_bus_bw_compute_timer_try_stop(hdd_context_t *hdd_ctx)
{
return;
}
static inline void hdd_stop_bus_bw_computer_timer(hdd_adapter_t *pAdapter)
{
return;
}
static inline int hdd_bus_bandwidth_init(hdd_context_t *hdd_ctx)
@@ -1802,7 +1848,6 @@ static inline int hdd_bus_bandwidth_init(hdd_context_t *hdd_ctx)
static inline void hdd_bus_bandwidth_destroy(hdd_context_t *hdd_ctx)
{
return;
}
#endif

View File

@@ -1306,7 +1306,7 @@ static void hdd_send_association_event(struct net_device *dev,
&pAdapter->prev_fwd_tx_packets,
&pAdapter->prev_fwd_rx_packets);
spin_unlock_bh(&pHddCtx->bus_bw_lock);
hdd_start_bus_bw_compute_timer(pAdapter);
hdd_bus_bw_compute_timer_start(pHddCtx);
#endif
#endif
} else if (eConnectionState_IbssConnected == /* IBss Associated */
@@ -1371,7 +1371,7 @@ static void hdd_send_association_event(struct net_device *dev,
pAdapter->prev_fwd_tx_packets = 0;
pAdapter->prev_fwd_rx_packets = 0;
spin_unlock_bh(&pHddCtx->bus_bw_lock);
hdd_stop_bus_bw_compute_timer(pAdapter);
hdd_bus_bw_compute_timer_try_stop(pHddCtx);
#endif
}
hdd_ipa_set_tx_flow_info();

View File

@@ -1601,7 +1601,7 @@ QDF_STATUS hdd_hostapd_sap_event_cb(tpSap_Event pSapEvent,
&pHostapdAdapter->prev_fwd_rx_packets);
spin_unlock_bh(&pHddCtx->bus_bw_lock);
hdd_start_bus_bw_compute_timer(pHostapdAdapter);
hdd_bus_bw_compute_timer_start(pHddCtx);
}
#endif
pHddApCtx->bApActive = true;
@@ -1815,7 +1815,7 @@ QDF_STATUS hdd_hostapd_sap_event_cb(tpSap_Event pSapEvent,
pHostapdAdapter->prev_fwd_tx_packets = 0;
pHostapdAdapter->prev_fwd_rx_packets = 0;
spin_unlock_bh(&pHddCtx->bus_bw_lock);
hdd_stop_bus_bw_compute_timer(pHostapdAdapter);
hdd_bus_bw_compute_timer_try_stop(pHddCtx);
}
#endif
hdd_green_ap_del_sta(pHddCtx);

View File

@@ -3788,15 +3788,20 @@ 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");
hdd_bus_bw_compute_timer_stop(hdd_ctx);
cancel_work_sync(&hdd_ctx->bus_bw_work);
/* cleanup adapter */
policy_mgr_clear_concurrency_mode(hdd_ctx->hdd_psoc,
adapter->device_mode);
hdd_cleanup_adapter(hdd_ctx, adapterNode->pAdapter, rtnl_held);
hdd_remove_adapter(hdd_ctx, adapterNode);
qdf_mem_free(adapterNode);
adapterNode = NULL;
/* conditionally restart the bw timer */
hdd_bus_bw_compute_timer_try_start(hdd_ctx);
/* Adapter removed. Decrement vdev count */
if (hdd_ctx->current_intf_count != 0)
hdd_ctx->current_intf_count--;
@@ -9668,76 +9673,134 @@ hdd_adapter_t *hdd_get_con_sap_adapter(hdd_adapter_t *this_sap_adapter,
}
#ifdef MSM_PLATFORM
void hdd_start_bus_bw_compute_timer(hdd_adapter_t *adapter)
static inline bool hdd_adapter_is_sta(hdd_adapter_t *adapter)
{
hdd_context_t *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
return adapter->device_mode == QDF_STA_MODE ||
adapter->device_mode == QDF_P2P_CLIENT_MODE;
}
static inline bool hdd_adapter_is_ap(hdd_adapter_t *adapter)
{
return adapter->device_mode == QDF_SAP_MODE ||
adapter->device_mode == QDF_P2P_GO_MODE;
}
static bool hdd_any_adapter_is_assoc(hdd_context_t *hdd_ctx)
{
QDF_STATUS status;
hdd_adapter_list_node_t *node;
status = hdd_get_front_adapter(hdd_ctx, &node);
while (QDF_IS_STATUS_SUCCESS(status) && node) {
hdd_adapter_t *adapter = node->pAdapter;
if (adapter &&
hdd_adapter_is_sta(adapter) &&
WLAN_HDD_GET_STATION_CTX_PTR(adapter)->
conn_info.connState == eConnectionState_Associated) {
return true;
}
if (adapter &&
hdd_adapter_is_ap(adapter) &&
WLAN_HDD_GET_AP_CTX_PTR(adapter)->bApActive) {
return true;
}
status = hdd_get_next_adapter(hdd_ctx, node, &node);
}
return false;
}
static bool hdd_bus_bw_compute_timer_is_running(hdd_context_t *hdd_ctx)
{
bool is_running;
qdf_spinlock_acquire(&hdd_ctx->bus_bw_timer_lock);
if (!hdd_ctx->bus_bw_timer_running) {
is_running = hdd_ctx->bus_bw_timer_running;
qdf_spinlock_release(&hdd_ctx->bus_bw_timer_lock);
return is_running;
}
static void __hdd_bus_bw_compute_timer_start(hdd_context_t *hdd_ctx)
{
qdf_spinlock_acquire(&hdd_ctx->bus_bw_timer_lock);
hdd_ctx->bus_bw_timer_running = true;
qdf_timer_start(&hdd_ctx->bus_bw_timer,
hdd_ctx->config->busBandwidthComputeInterval);
}
qdf_spinlock_release(&hdd_ctx->bus_bw_timer_lock);
}
void hdd_stop_bus_bw_compute_timer(hdd_adapter_t *adapter)
void hdd_bus_bw_compute_timer_start(hdd_context_t *hdd_ctx)
{
hdd_adapter_list_node_t *adapterNode = NULL, *pNext = NULL;
QDF_STATUS status;
bool can_stop = true;
hdd_context_t *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
ENTER();
qdf_spinlock_acquire(&hdd_ctx->bus_bw_timer_lock);
if (!hdd_ctx->bus_bw_timer_running) {
qdf_spinlock_release(&hdd_ctx->bus_bw_timer_lock);
/* trying to stop timer, when not running is not good */
hdd_info("bus band width compute timer is not running");
if (hdd_bus_bw_compute_timer_is_running(hdd_ctx)) {
hdd_debug("Bandwidth compute timer already started");
return;
}
qdf_spinlock_release(&hdd_ctx->bus_bw_timer_lock);
if (policy_mgr_concurrent_open_sessions_running(
hdd_ctx->hdd_psoc)) {
status = hdd_get_front_adapter(hdd_ctx, &adapterNode);
__hdd_bus_bw_compute_timer_start(hdd_ctx);
while (NULL != adapterNode && QDF_STATUS_SUCCESS == status) {
adapter = adapterNode->pAdapter;
if (adapter
&& (adapter->device_mode == QDF_STA_MODE
|| adapter->device_mode == QDF_P2P_CLIENT_MODE)
&& WLAN_HDD_GET_STATION_CTX_PTR(adapter)->
conn_info.connState ==
eConnectionState_Associated) {
can_stop = false;
break;
}
if (adapter
&& (adapter->device_mode == QDF_SAP_MODE
|| adapter->device_mode == QDF_P2P_GO_MODE)
&& WLAN_HDD_GET_AP_CTX_PTR(adapter)->bApActive ==
true) {
can_stop = false;
break;
}
status = hdd_get_next_adapter(hdd_ctx,
adapterNode,
&pNext);
adapterNode = pNext;
}
EXIT();
}
if (can_stop == true) {
/* reset the ipa perf level */
void hdd_bus_bw_compute_timer_try_start(hdd_context_t *hdd_ctx)
{
ENTER();
if (hdd_bus_bw_compute_timer_is_running(hdd_ctx)) {
hdd_debug("Bandwidth compute timer already started");
return;
}
if (hdd_any_adapter_is_assoc(hdd_ctx))
__hdd_bus_bw_compute_timer_start(hdd_ctx);
EXIT();
}
static void __hdd_bus_bw_compute_timer_stop(hdd_context_t *hdd_ctx)
{
hdd_ipa_set_perf_level(hdd_ctx, 0, 0);
qdf_spinlock_acquire(&hdd_ctx->bus_bw_timer_lock);
qdf_timer_stop(&hdd_ctx->bus_bw_timer);
hdd_ctx->bus_bw_timer_running = false;
qdf_spinlock_release(&hdd_ctx->bus_bw_timer_lock);
hdd_reset_tcp_delack(hdd_ctx);
}
void hdd_bus_bw_compute_timer_stop(hdd_context_t *hdd_ctx)
{
ENTER();
if (!hdd_bus_bw_compute_timer_is_running(hdd_ctx)) {
hdd_debug("Bandwidth compute timer already stopped");
return;
}
__hdd_bus_bw_compute_timer_stop(hdd_ctx);
EXIT();
}
void hdd_bus_bw_compute_timer_try_stop(hdd_context_t *hdd_ctx)
{
ENTER();
if (!hdd_bus_bw_compute_timer_is_running(hdd_ctx)) {
hdd_debug("Bandwidth compute timer already stopped");
return;
}
if (!hdd_any_adapter_is_assoc(hdd_ctx))
__hdd_bus_bw_compute_timer_stop(hdd_ctx);
EXIT();
}
#endif