qcacld-3.0: HDD IPA event handler waits resource load/unload complete

There is a race condition bewteen CLIENT_DISCONNECT and AP_DISCONNECT,
which causes short HDD IPA interfaces, so AP_CONNECT failed afterward.
HDD IPA event handler waits IPA resource load/unload complete.
Once timed out, add the event in a pending queue for loading icase or
ignore it for unloading case.
Remove redundant CLIENT_DISCONNECT event from del_sta.

Change-Id: I671ecec6f236d52a1b51cb78b9c7466f2cde82aa
CRs-Fixed: 2027714
This commit is contained in:
Yun Park
2017-03-30 15:38:33 -07:00
committed by snandini
parent 76b9212c95
commit 777d724e77

View File

@@ -494,6 +494,7 @@ struct hdd_ipa_priv {
struct completion ipa_uc_sharing_stats_comp; struct completion ipa_uc_sharing_stats_comp;
struct completion ipa_uc_set_quota_comp; struct completion ipa_uc_set_quota_comp;
#endif #endif
struct completion ipa_resource_comp;
}; };
#define HDD_IPA_WLAN_FRAG_HEADER sizeof(struct frag_header) #define HDD_IPA_WLAN_FRAG_HEADER sizeof(struct frag_header)
@@ -560,6 +561,8 @@ static struct hdd_ipa_adapter_2_client {
#define IPA_UC_SET_QUOTA_WAIT_TIME 500 #define IPA_UC_SET_QUOTA_WAIT_TIME 500
#endif #endif
#define IPA_RESOURCE_COMP_WAIT_TIME 100
static struct hdd_ipa_priv *ghdd_ipa; static struct hdd_ipa_priv *ghdd_ipa;
/* Local Function Prototypes */ /* Local Function Prototypes */
@@ -1863,6 +1866,7 @@ static int hdd_ipa_uc_enable_pipes(struct hdd_ipa_priv *hdd_ipa)
return result; return result;
} }
INIT_COMPLETION(hdd_ipa->ipa_resource_comp);
hdd_ipa->ipa_pipes_down = false; hdd_ipa->ipa_pipes_down = false;
cdp_ipa_enable_autonomy(soc, (struct cdp_pdev *)pdev); cdp_ipa_enable_autonomy(soc, (struct cdp_pdev *)pdev);
@@ -1960,6 +1964,7 @@ static void hdd_ipa_uc_handle_last_discon(struct hdd_ipa_priv *hdd_ipa)
} }
hdd_ipa->resource_unloading = true; hdd_ipa->resource_unloading = true;
INIT_COMPLETION(hdd_ipa->ipa_resource_comp);
HDD_IPA_LOG(QDF_TRACE_LEVEL_DEBUG, "Disable FW RX PIPE"); HDD_IPA_LOG(QDF_TRACE_LEVEL_DEBUG, "Disable FW RX PIPE");
cdp_ipa_set_active(soc, (struct cdp_pdev *)pdev, false, false); cdp_ipa_set_active(soc, (struct cdp_pdev *)pdev, false, false);
HDD_IPA_LOG(QDF_TRACE_LEVEL_DEBUG, "Disable FW TX PIPE"); HDD_IPA_LOG(QDF_TRACE_LEVEL_DEBUG, "Disable FW TX PIPE");
@@ -2238,6 +2243,7 @@ static void hdd_ipa_uc_op_cb(struct op_msg_type *op_msg, void *usr_ctxt)
hdd_ipa->activated_fw_pipe++; hdd_ipa->activated_fw_pipe++;
if (HDD_IPA_UC_NUM_WDI_PIPE == hdd_ipa->activated_fw_pipe) { if (HDD_IPA_UC_NUM_WDI_PIPE == hdd_ipa->activated_fw_pipe) {
hdd_ipa->resource_loading = false; hdd_ipa->resource_loading = false;
complete(&hdd_ipa->ipa_resource_comp);
if (hdd_ipa->wdi_enabled == false) { if (hdd_ipa->wdi_enabled == false) {
hdd_ipa->wdi_enabled = true; hdd_ipa->wdi_enabled = true;
if (hdd_ipa_uc_send_wdi_control_msg(true) == 0) if (hdd_ipa_uc_send_wdi_control_msg(true) == 0)
@@ -2257,15 +2263,16 @@ static void hdd_ipa_uc_op_cb(struct op_msg_type *op_msg, void *usr_ctxt)
qdf_mutex_acquire(&hdd_ipa->ipa_lock); qdf_mutex_acquire(&hdd_ipa->ipa_lock);
hdd_ipa->activated_fw_pipe--; hdd_ipa->activated_fw_pipe--;
if (!hdd_ipa->activated_fw_pipe) { if (!hdd_ipa->activated_fw_pipe) {
/*
* Async return success from FW
* Disable/suspend all the PIPEs
*/
hdd_ipa_uc_disable_pipes(hdd_ipa); hdd_ipa_uc_disable_pipes(hdd_ipa);
if (hdd_ipa_is_rm_enabled(hdd_ipa->hdd_ctx)) if (hdd_ipa_is_rm_enabled(hdd_ipa->hdd_ctx))
ipa_rm_release_resource( ipa_rm_release_resource(
IPA_RM_RESOURCE_WLAN_PROD); IPA_RM_RESOURCE_WLAN_PROD);
/*
* Sync return success from IPA
* Enable/resume all the PIPEs
*/
hdd_ipa->resource_unloading = false; hdd_ipa->resource_unloading = false;
complete(&hdd_ipa->ipa_resource_comp);
hdd_ipa_uc_proc_pending_event(hdd_ipa); hdd_ipa_uc_proc_pending_event(hdd_ipa);
hdd_ipa->pending_cons_req = false; hdd_ipa->pending_cons_req = false;
} }
@@ -2656,7 +2663,6 @@ static void __hdd_ipa_wdi_meter_notifier_cb(enum ipa_wdi_meter_evt_type evt,
} }
INIT_COMPLETION(hdd_ipa->ipa_uc_sharing_stats_comp); INIT_COMPLETION(hdd_ipa->ipa_uc_sharing_stats_comp);
INIT_COMPLETION(hdd_ipa->ipa_uc_set_quota_comp);
hdd_ipa_uc_sharing_stats_request(adapter, hdd_ipa_uc_sharing_stats_request(adapter,
wdi_sap_stats->reset_stats); wdi_sap_stats->reset_stats);
ret = wait_for_completion_timeout( ret = wait_for_completion_timeout(
@@ -2711,6 +2717,7 @@ static void __hdd_ipa_wdi_meter_notifier_cb(enum ipa_wdi_meter_evt_type evt,
return; return;
} }
INIT_COMPLETION(hdd_ipa->ipa_uc_set_quota_comp);
hdd_ipa_uc_set_quota(adapter, ipa_set_quota->set_quota, hdd_ipa_uc_set_quota(adapter, ipa_set_quota->set_quota,
ipa_set_quota->quota_bytes); ipa_set_quota->quota_bytes);
@@ -4930,52 +4937,73 @@ static int __hdd_ipa_wlan_evt(struct hdd_adapter *adapter, uint8_t sta_id,
/* /*
* During IPA UC resource loading/unloading new events can be issued. * During IPA UC resource loading/unloading new events can be issued.
* Store the events separately and handle them later.
*/ */
if (hdd_ipa_uc_is_enabled(hdd_ipa->hdd_ctx)) { if (hdd_ipa_uc_is_enabled(hdd_ipa->hdd_ctx) &&
if (hdd_ipa->resource_loading) { (hdd_ipa->resource_loading || hdd_ipa->resource_unloading)) {
unsigned int pending_event_count; unsigned int pending_event_count;
struct ipa_uc_pending_event *pending_event = NULL; struct ipa_uc_pending_event *pending_event = NULL;
HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
"%s:IPA resource %s inprogress",
hdd_ipa_wlan_event_to_str(type),
hdd_ipa->resource_loading ?
"load" : "unload");
/* Wait until completion of the long/unloading */
ret = wait_for_completion_timeout(&hdd_ipa->ipa_resource_comp,
msecs_to_jiffies(IPA_RESOURCE_COMP_WAIT_TIME));
if (!ret) {
/*
* If timed out, store the events separately and
* handle them later.
*/
HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
"IPA resource load in progress"); "IPA resource %s timed out",
hdd_ipa->resource_loading ?
"load" : "unload");
qdf_mutex_acquire(&hdd_ipa->ipa_lock); if (hdd_ipa->resource_loading) {
qdf_mutex_acquire(&hdd_ipa->ipa_lock);
pending_event_count = pending_event_count =
qdf_list_size(&hdd_ipa->pending_event); qdf_list_size(&hdd_ipa->pending_event);
if (pending_event_count >= if (pending_event_count >=
HDD_IPA_MAX_PENDING_EVENT_COUNT) { HDD_IPA_MAX_PENDING_EVENT_COUNT) {
hdd_debug("Reached max pending event count"); hdd_debug(
qdf_list_remove_front(&hdd_ipa->pending_event, "Reached max pending event count");
(qdf_list_node_t **)&pending_event); qdf_list_remove_front(
} else { &hdd_ipa->pending_event,
pending_event = (qdf_list_node_t **)&pending_event);
qdf_mem_malloc(sizeof(*pending_event)); } else {
} pending_event =
(struct ipa_uc_pending_event *)
qdf_mem_malloc(sizeof(
struct ipa_uc_pending_event));
}
if (!pending_event) {
HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
"Pending event memory alloc fail");
qdf_mutex_release(&hdd_ipa->ipa_lock);
return -ENOMEM;
}
pending_event->adapter = adapter;
pending_event->sta_id = sta_id;
pending_event->type = type;
qdf_mem_copy(pending_event->mac_addr,
mac_addr, QDF_MAC_ADDR_SIZE);
qdf_list_insert_back(&hdd_ipa->pending_event,
&pending_event->node);
if (!pending_event) {
qdf_mutex_release(&hdd_ipa->ipa_lock); qdf_mutex_release(&hdd_ipa->ipa_lock);
HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
"Pending event memory alloc fail");
return -ENOMEM;
} }
pending_event->adapter = adapter;
pending_event->sta_id = sta_id;
pending_event->type = type;
qdf_mem_copy(pending_event->mac_addr,
mac_addr,
QDF_MAC_ADDR_SIZE);
qdf_list_insert_back(&hdd_ipa->pending_event,
&pending_event->node);
qdf_mutex_release(&hdd_ipa->ipa_lock);
return 0; return 0;
} else if (hdd_ipa->resource_unloading) { } else {
HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
"IPA resource unload in progress"); "IPA resource %s completed",
return 0; hdd_ipa->resource_loading ?
"load" : "unload");
} }
} }
@@ -5509,6 +5537,8 @@ static QDF_STATUS __hdd_ipa_init(struct hdd_context *hdd_ctx)
} }
} }
init_completion(&hdd_ipa->ipa_resource_comp);
EXIT(); EXIT();
return QDF_STATUS_SUCCESS; return QDF_STATUS_SUCCESS;