diff options
| author | Arunk Khandavalli <akhandav@codeaurora.org> | 2017-10-04 19:56:46 +0530 |
|---|---|---|
| committer | snandini <snandini@codeaurora.org> | 2017-10-17 19:27:19 -0700 |
| commit | 54284d380f6f6cb81f3f76c57d0a05bd7fa87cd3 (patch) | |
| tree | 9455a7525d7415c6524afa7a3f33910dec7923f7 | |
| parent | 6aadcd58b5cf78aa8281f34af6a73ba0f36e20bb (diff) | |
qcacld-3.0: cleanup scan queue in case of SSR
When the SSR and interface down happen in parallel,
the driver rejects the interface down since the
recovery is in progress. Kernel ignores the -EAGAIN
request from the driver and as part of NET_DOWN notification
in cfg80211_netdown_notifer the kernel invokes the
___cfg80211_scan_done to free the request but dose not send
scan_result indication, since it expects the scan_done work to
get scheduled and then broadcast the request to upperlayer.
According to the upstream commit f9d15d162b3acf28f85b3ac05c4883e5ed588d28
the reason the kernel doesnt broadcast the scan result to upperlayer
is that:
1. interface starts being removed
2. scan is cancelled by driver and cfg80211 is notified
3. scan done work is scheduled
4. interface is removed completely, rdev->scan_req is freed,
event sent to userspace but scan done work remains pending
5. new scan is requested on another virtual interface
6. scan done work runs, freeing the still-running scan
To mitigate the issue, clean up the scan queue as part of netdev
going down and FW down event.
Change-Id: Id698044ebe30cf555300e909a916d74be1f8b94f
CRs-Fixed: 2115392
| -rw-r--r-- | core/hdd/src/wlan_hdd_driver_ops.c | 28 | ||||
| -rw-r--r-- | core/hdd/src/wlan_hdd_main.c | 12 | ||||
| -rw-r--r-- | core/hdd/src/wlan_hdd_power.c | 1 | ||||
| -rw-r--r-- | core/hdd/src/wlan_hdd_scan.c | 93 | ||||
| -rw-r--r-- | core/hdd/src/wlan_hdd_scan.h | 2 |
5 files changed, 99 insertions, 37 deletions
diff --git a/core/hdd/src/wlan_hdd_driver_ops.c b/core/hdd/src/wlan_hdd_driver_ops.c index 6cf9a0ac58eb..c8da11f2b735 100644 --- a/core/hdd/src/wlan_hdd_driver_ops.c +++ b/core/hdd/src/wlan_hdd_driver_ops.c @@ -46,6 +46,7 @@ #include "cdp_txrx_bus.h" #include "pld_common.h" #include "wlan_hdd_driver_ops.h" +#include "wlan_hdd_scan.h" #ifdef MODULE #define WLAN_MODULE_NAME module_name(THIS_MODULE) @@ -1321,6 +1322,29 @@ static void wlan_hdd_purge_notifier(void) EXIT(); } + +/** + * hdd_cleanup_on_fw_down() - cleanup on FW down event + * + * Return: void + */ +static void hdd_cleanup_on_fw_down(void) +{ + hdd_context_t *hdd_ctx; + + ENTER(); + + hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD); + cds_set_fw_state(CDS_FW_STATE_DOWN); + cds_set_target_ready(false); + if (hdd_ctx != NULL) + hdd_cleanup_scan_queue(hdd_ctx, NULL); + wlan_hdd_purge_notifier(); + + EXIT(); + +} + /** * wlan_hdd_pld_uevent() - update driver status * @dev: device @@ -1340,9 +1364,7 @@ static void wlan_hdd_pld_uevent(struct device *dev, wlan_hdd_purge_notifier(); break; case PLD_FW_DOWN: - cds_set_fw_state(CDS_FW_STATE_DOWN); - cds_set_target_ready(false); - wlan_hdd_purge_notifier(); + hdd_cleanup_on_fw_down(); break; case PLD_FW_READY: cds_set_target_ready(true); diff --git a/core/hdd/src/wlan_hdd_main.c b/core/hdd/src/wlan_hdd_main.c index 20a642afe239..72de42d8439a 100644 --- a/core/hdd/src/wlan_hdd_main.c +++ b/core/hdd/src/wlan_hdd_main.c @@ -509,6 +509,14 @@ static int __hdd_netdev_notifier_call(struct notifier_block *nb, cds_flush_work(&adapter->scan_block_work); hdd_debug("Scan is not Pending from user"); } + /* + * After NETDEV_GOING_DOWN, kernel calls hdd_stop.Irrespective + * of return status of hdd_stop call, kernel resets the IFF_UP + * flag after which driver does not send the cfg80211_scan_done. + * Ensure to cleanup the scan queue in NETDEV_GOING_DOWN + */ + + hdd_cleanup_scan_queue(hdd_ctx, adapter); break; default: @@ -5925,7 +5933,7 @@ static void hdd_wlan_exit(hdd_context_t *hdd_ctx) * the expectation is that by the time Request Full Power has * completed, all scans will be cancelled */ - hdd_cleanup_scan_queue(hdd_ctx); + hdd_cleanup_scan_queue(hdd_ctx, NULL); hdd_abort_mac_scan_all_adapters(hdd_ctx); hdd_abort_sched_scan_all_adapters(hdd_ctx); hdd_stop_all_adapters(hdd_ctx, true); @@ -11808,7 +11816,7 @@ static void hdd_stop_present_mode(hdd_context_t *hdd_ctx, case QDF_GLOBAL_MONITOR_MODE: case QDF_GLOBAL_FTM_MODE: hdd_abort_mac_scan_all_adapters(hdd_ctx); - hdd_cleanup_scan_queue(hdd_ctx); + hdd_cleanup_scan_queue(hdd_ctx, NULL); /* re-use the existing session */ hdd_stop_all_adapters(hdd_ctx, false); diff --git a/core/hdd/src/wlan_hdd_power.c b/core/hdd/src/wlan_hdd_power.c index 52368d812b43..c6b31adf72de 100644 --- a/core/hdd/src/wlan_hdd_power.c +++ b/core/hdd/src/wlan_hdd_power.c @@ -1455,7 +1455,6 @@ QDF_STATUS hdd_wlan_shutdown(void) hdd_debug("Invoking packetdump deregistration API"); wlan_deregister_txrx_packetdump(); - hdd_cleanup_scan_queue(pHddCtx); hdd_reset_all_adapters(pHddCtx); /* Flush cached rx frame queue */ diff --git a/core/hdd/src/wlan_hdd_scan.c b/core/hdd/src/wlan_hdd_scan.c index fa27fd8e4745..ae67337f113e 100644 --- a/core/hdd/src/wlan_hdd_scan.c +++ b/core/hdd/src/wlan_hdd_scan.c @@ -1367,6 +1367,9 @@ static void hdd_cfg80211_scan_done(hdd_adapter_t *adapter, if (adapter->dev->flags & IFF_UP) cfg80211_scan_done(req, &info); + else + hdd_debug("IFF_UP flag reset for %s", adapter->dev->name); + } #elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)) /** @@ -1385,6 +1388,8 @@ static void hdd_cfg80211_scan_done(hdd_adapter_t *adapter, { if (adapter->dev->flags & IFF_UP) cfg80211_scan_done(req, aborted); + else + hdd_debug("IFF_UP flag reset for %s", adapter->dev->name); } #else /** @@ -3709,16 +3714,16 @@ void wlan_hdd_cfg80211_abort_scan(struct wiphy *wiphy, * Removes entries in scan queue and sends scan complete event to NL * Return: None */ -void hdd_cleanup_scan_queue(hdd_context_t *hdd_ctx) +void hdd_cleanup_scan_queue(hdd_context_t *hdd_ctx, hdd_adapter_t *padapter) { struct hdd_scan_req *hdd_scan_req; - qdf_list_node_t *node = NULL; + qdf_list_node_t *node = NULL, *next_node = NULL; struct cfg80211_scan_request *req; hdd_adapter_t *adapter; uint8_t source; bool aborted = true; QDF_STATUS status; - hdd_adapter_list_node_t *adapter_node = NULL, *next_node = NULL; + hdd_adapter_list_node_t *adapter_node = NULL, *next_adapter_node = NULL; if (NULL == hdd_ctx) { hdd_err("HDD context is Null"); @@ -3726,51 +3731,79 @@ void hdd_cleanup_scan_queue(hdd_context_t *hdd_ctx) } qdf_spin_lock(&hdd_ctx->hdd_scan_req_q_lock); - while (!qdf_list_empty(&hdd_ctx->hdd_scan_req_q)) { - if (QDF_STATUS_SUCCESS != - qdf_list_remove_front(&hdd_ctx->hdd_scan_req_q, - &node)) { - qdf_spin_unlock(&hdd_ctx->hdd_scan_req_q_lock); - hdd_err("Failed to remove scan request"); - return; - } + + if (qdf_list_empty(&hdd_ctx->hdd_scan_req_q)) { qdf_spin_unlock(&hdd_ctx->hdd_scan_req_q_lock); - hdd_scan_req = container_of(node, struct hdd_scan_req, node); + return; + } + + status = qdf_list_peek_front(&hdd_ctx->hdd_scan_req_q, &node); + if (status != QDF_STATUS_SUCCESS) { + qdf_spin_unlock(&hdd_ctx->hdd_scan_req_q_lock); + hdd_err("Failed to peek the Scan Req from queue"); + return; + } + while (node != NULL) { + qdf_list_peek_next(&hdd_ctx->hdd_scan_req_q, node, + &next_node); + + hdd_scan_req = qdf_container_of(node, + struct hdd_scan_req, node); req = hdd_scan_req->scan_request; source = hdd_scan_req->source; adapter = hdd_scan_req->adapter; - qdf_timer_stop(&hdd_scan_req->hdd_scan_inactivity_timer); - qdf_timer_free(&hdd_scan_req->hdd_scan_inactivity_timer); - hdd_debug("Stopping HDD Scan inactivity timer"); + if (!padapter || (padapter == adapter)) { - if (WLAN_HDD_ADAPTER_MAGIC != adapter->magic) { - hdd_err("HDD adapter magic is invalid"); - } else if (!req) { - hdd_debug("pending scan is wext triggered"); - } else { - if (NL_SCAN == source) - hdd_cfg80211_scan_done(adapter, req, aborted); - else - hdd_vendor_scan_callback(adapter, req, aborted); - hdd_debug("removed Scan id: %d, req = %pK", + status = qdf_list_remove_node(&hdd_ctx->hdd_scan_req_q, + node); + if (QDF_IS_STATUS_ERROR(status)) { + qdf_spin_unlock(&hdd_ctx->hdd_scan_req_q_lock); + hdd_err("Failed to remove scan request from queue"); + return; + } + + qdf_spin_unlock(&hdd_ctx->hdd_scan_req_q_lock); + + qdf_timer_stop( + &hdd_scan_req->hdd_scan_inactivity_timer); + qdf_timer_free( + &hdd_scan_req->hdd_scan_inactivity_timer); + + hdd_debug("Stopping HDD Scan inactivity timer"); + if (WLAN_HDD_ADAPTER_MAGIC != adapter->magic) { + hdd_err("HDD adapter magic is invalid"); + } else if (!req) { + hdd_debug("pending scan is wext triggered"); + } else { + if (NL_SCAN == source) + hdd_cfg80211_scan_done(adapter, + req, aborted); + else + hdd_vendor_scan_callback(adapter, + req, aborted); + hdd_debug("removed Scan id: %d, req = %pK", hdd_scan_req->scan_id, req); + } + qdf_mem_free(hdd_scan_req); + qdf_spin_lock(&hdd_ctx->hdd_scan_req_q_lock); } - qdf_mem_free(hdd_scan_req); - qdf_spin_lock(&hdd_ctx->hdd_scan_req_q_lock); + node = next_node; + next_node = NULL; } qdf_spin_unlock(&hdd_ctx->hdd_scan_req_q_lock); status = hdd_get_front_adapter(hdd_ctx, &adapter_node); while (NULL != adapter_node && QDF_IS_STATUS_SUCCESS(status)) { adapter = adapter_node->pAdapter; - if (adapter->scan_info.mScanPending) { + if ((!padapter || (padapter == adapter)) && + (adapter->scan_info.mScanPending)) { adapter->scan_info.mScanPending = false; complete(&adapter->scan_info.abortscan_event_var); } status = hdd_get_next_adapter(hdd_ctx, adapter_node, - &next_node); - adapter_node = next_node; + &next_adapter_node); + adapter_node = next_adapter_node; } } diff --git a/core/hdd/src/wlan_hdd_scan.h b/core/hdd/src/wlan_hdd_scan.h index d250e746cb54..4b4bdc5db57a 100644 --- a/core/hdd/src/wlan_hdd_scan.h +++ b/core/hdd/src/wlan_hdd_scan.h @@ -145,7 +145,7 @@ int wlan_hdd_vendor_abort_scan( struct wiphy *wiphy, struct wireless_dev *wdev, const void *data, int data_len); -void hdd_cleanup_scan_queue(hdd_context_t *hdd_ctx); +void hdd_cleanup_scan_queue(hdd_context_t *hdd_ctx, hdd_adapter_t *p_adapter); #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 5, 0)) || \ defined(CFG80211_ABORT_SCAN) |
