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:

committed by
Sandeep Puligilla

parent
14a8e0312e
commit
5ec6b5598e
@@ -1762,8 +1762,43 @@ hdd_wlan_get_ibss_mac_addr_from_staid(hdd_adapter_t *pAdapter,
|
|||||||
uint8_t staIdx);
|
uint8_t staIdx);
|
||||||
void hdd_checkandupdate_phymode(hdd_context_t *pHddCtx);
|
void hdd_checkandupdate_phymode(hdd_context_t *pHddCtx);
|
||||||
#ifdef MSM_PLATFORM
|
#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.
|
* 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);
|
void hdd_bus_bandwidth_destroy(hdd_context_t *hdd_ctx);
|
||||||
#else
|
#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)
|
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)
|
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)
|
static inline void hdd_bus_bandwidth_destroy(hdd_context_t *hdd_ctx)
|
||||||
{
|
{
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@@ -1306,7 +1306,7 @@ static void hdd_send_association_event(struct net_device *dev,
|
|||||||
&pAdapter->prev_fwd_tx_packets,
|
&pAdapter->prev_fwd_tx_packets,
|
||||||
&pAdapter->prev_fwd_rx_packets);
|
&pAdapter->prev_fwd_rx_packets);
|
||||||
spin_unlock_bh(&pHddCtx->bus_bw_lock);
|
spin_unlock_bh(&pHddCtx->bus_bw_lock);
|
||||||
hdd_start_bus_bw_compute_timer(pAdapter);
|
hdd_bus_bw_compute_timer_start(pHddCtx);
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
} else if (eConnectionState_IbssConnected == /* IBss Associated */
|
} 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_tx_packets = 0;
|
||||||
pAdapter->prev_fwd_rx_packets = 0;
|
pAdapter->prev_fwd_rx_packets = 0;
|
||||||
spin_unlock_bh(&pHddCtx->bus_bw_lock);
|
spin_unlock_bh(&pHddCtx->bus_bw_lock);
|
||||||
hdd_stop_bus_bw_compute_timer(pAdapter);
|
hdd_bus_bw_compute_timer_try_stop(pHddCtx);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
hdd_ipa_set_tx_flow_info();
|
hdd_ipa_set_tx_flow_info();
|
||||||
|
@@ -1601,7 +1601,7 @@ QDF_STATUS hdd_hostapd_sap_event_cb(tpSap_Event pSapEvent,
|
|||||||
&pHostapdAdapter->prev_fwd_rx_packets);
|
&pHostapdAdapter->prev_fwd_rx_packets);
|
||||||
|
|
||||||
spin_unlock_bh(&pHddCtx->bus_bw_lock);
|
spin_unlock_bh(&pHddCtx->bus_bw_lock);
|
||||||
hdd_start_bus_bw_compute_timer(pHostapdAdapter);
|
hdd_bus_bw_compute_timer_start(pHddCtx);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
pHddApCtx->bApActive = true;
|
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_tx_packets = 0;
|
||||||
pHostapdAdapter->prev_fwd_rx_packets = 0;
|
pHostapdAdapter->prev_fwd_rx_packets = 0;
|
||||||
spin_unlock_bh(&pHddCtx->bus_bw_lock);
|
spin_unlock_bh(&pHddCtx->bus_bw_lock);
|
||||||
hdd_stop_bus_bw_compute_timer(pHostapdAdapter);
|
hdd_bus_bw_compute_timer_try_stop(pHddCtx);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
hdd_green_ap_del_sta(pHddCtx);
|
hdd_green_ap_del_sta(pHddCtx);
|
||||||
|
@@ -3788,15 +3788,20 @@ QDF_STATUS hdd_close_adapter(hdd_context_t *hdd_ctx, hdd_adapter_t *adapter,
|
|||||||
adapterNode = pCurrent;
|
adapterNode = pCurrent;
|
||||||
if (QDF_STATUS_SUCCESS == status) {
|
if (QDF_STATUS_SUCCESS == status) {
|
||||||
hdd_info("wait for bus bw work to flush");
|
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);
|
cancel_work_sync(&hdd_ctx->bus_bw_work);
|
||||||
|
|
||||||
|
/* cleanup adapter */
|
||||||
policy_mgr_clear_concurrency_mode(hdd_ctx->hdd_psoc,
|
policy_mgr_clear_concurrency_mode(hdd_ctx->hdd_psoc,
|
||||||
adapter->device_mode);
|
adapter->device_mode);
|
||||||
hdd_cleanup_adapter(hdd_ctx, adapterNode->pAdapter, rtnl_held);
|
hdd_cleanup_adapter(hdd_ctx, adapterNode->pAdapter, rtnl_held);
|
||||||
|
|
||||||
hdd_remove_adapter(hdd_ctx, adapterNode);
|
hdd_remove_adapter(hdd_ctx, adapterNode);
|
||||||
qdf_mem_free(adapterNode);
|
qdf_mem_free(adapterNode);
|
||||||
adapterNode = NULL;
|
adapterNode = NULL;
|
||||||
|
|
||||||
|
/* conditionally restart the bw timer */
|
||||||
|
hdd_bus_bw_compute_timer_try_start(hdd_ctx);
|
||||||
|
|
||||||
/* Adapter removed. Decrement vdev count */
|
/* Adapter removed. Decrement vdev count */
|
||||||
if (hdd_ctx->current_intf_count != 0)
|
if (hdd_ctx->current_intf_count != 0)
|
||||||
hdd_ctx->current_intf_count--;
|
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
|
#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;
|
||||||
qdf_spinlock_acquire(&hdd_ctx->bus_bw_timer_lock);
|
|
||||||
if (!hdd_ctx->bus_bw_timer_running) {
|
|
||||||
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)
|
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)
|
||||||
{
|
{
|
||||||
hdd_adapter_list_node_t *adapterNode = NULL, *pNext = NULL;
|
|
||||||
QDF_STATUS status;
|
QDF_STATUS status;
|
||||||
bool can_stop = true;
|
hdd_adapter_list_node_t *node;
|
||||||
hdd_context_t *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
|
|
||||||
|
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);
|
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);
|
|
||||||
/* trying to stop timer, when not running is not good */
|
|
||||||
hdd_info("bus band width compute timer is not running");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
qdf_spinlock_release(&hdd_ctx->bus_bw_timer_lock);
|
qdf_spinlock_release(&hdd_ctx->bus_bw_timer_lock);
|
||||||
|
|
||||||
if (policy_mgr_concurrent_open_sessions_running(
|
return is_running;
|
||||||
hdd_ctx->hdd_psoc)) {
|
}
|
||||||
status = hdd_get_front_adapter(hdd_ctx, &adapterNode);
|
|
||||||
|
|
||||||
while (NULL != adapterNode && QDF_STATUS_SUCCESS == status) {
|
static void __hdd_bus_bw_compute_timer_start(hdd_context_t *hdd_ctx)
|
||||||
adapter = adapterNode->pAdapter;
|
{
|
||||||
if (adapter
|
qdf_spinlock_acquire(&hdd_ctx->bus_bw_timer_lock);
|
||||||
&& (adapter->device_mode == QDF_STA_MODE
|
hdd_ctx->bus_bw_timer_running = true;
|
||||||
|| adapter->device_mode == QDF_P2P_CLIENT_MODE)
|
qdf_timer_start(&hdd_ctx->bus_bw_timer,
|
||||||
&& WLAN_HDD_GET_STATION_CTX_PTR(adapter)->
|
hdd_ctx->config->busBandwidthComputeInterval);
|
||||||
conn_info.connState ==
|
qdf_spinlock_release(&hdd_ctx->bus_bw_timer_lock);
|
||||||
eConnectionState_Associated) {
|
}
|
||||||
can_stop = false;
|
|
||||||
break;
|
void hdd_bus_bw_compute_timer_start(hdd_context_t *hdd_ctx)
|
||||||
}
|
{
|
||||||
if (adapter
|
ENTER();
|
||||||
&& (adapter->device_mode == QDF_SAP_MODE
|
|
||||||
|| adapter->device_mode == QDF_P2P_GO_MODE)
|
if (hdd_bus_bw_compute_timer_is_running(hdd_ctx)) {
|
||||||
&& WLAN_HDD_GET_AP_CTX_PTR(adapter)->bApActive ==
|
hdd_debug("Bandwidth compute timer already started");
|
||||||
true) {
|
return;
|
||||||
can_stop = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
status = hdd_get_next_adapter(hdd_ctx,
|
|
||||||
adapterNode,
|
|
||||||
&pNext);
|
|
||||||
adapterNode = pNext;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (can_stop == true) {
|
__hdd_bus_bw_compute_timer_start(hdd_ctx);
|
||||||
/* reset the ipa perf level */
|
|
||||||
hdd_ipa_set_perf_level(hdd_ctx, 0, 0);
|
EXIT();
|
||||||
qdf_spinlock_acquire(&hdd_ctx->bus_bw_timer_lock);
|
}
|
||||||
qdf_timer_stop(&hdd_ctx->bus_bw_timer);
|
|
||||||
hdd_ctx->bus_bw_timer_running = false;
|
void hdd_bus_bw_compute_timer_try_start(hdd_context_t *hdd_ctx)
|
||||||
qdf_spinlock_release(&hdd_ctx->bus_bw_timer_lock);
|
{
|
||||||
hdd_reset_tcp_delack(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
|
#endif
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user