Browse Source

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
Yun Park 8 years ago
parent
commit
777d724e77
1 changed files with 70 additions and 40 deletions
  1. 70 40
      core/hdd/src/wlan_hdd_ipa.c

+ 70 - 40
core/hdd/src/wlan_hdd_ipa.c

@@ -494,6 +494,7 @@ struct hdd_ipa_priv {
 	struct completion ipa_uc_sharing_stats_comp;
 	struct completion ipa_uc_set_quota_comp;
 #endif
+	struct completion ipa_resource_comp;
 };
 
 #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
 #endif
 
+#define IPA_RESOURCE_COMP_WAIT_TIME	100
+
 static struct hdd_ipa_priv *ghdd_ipa;
 
 /* Local Function Prototypes */
@@ -1863,6 +1866,7 @@ static int hdd_ipa_uc_enable_pipes(struct hdd_ipa_priv *hdd_ipa)
 		return result;
 	}
 
+	INIT_COMPLETION(hdd_ipa->ipa_resource_comp);
 	hdd_ipa->ipa_pipes_down = false;
 
 	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;
+	INIT_COMPLETION(hdd_ipa->ipa_resource_comp);
 	HDD_IPA_LOG(QDF_TRACE_LEVEL_DEBUG, "Disable FW RX PIPE");
 	cdp_ipa_set_active(soc, (struct cdp_pdev *)pdev, false, false);
 	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++;
 		if (HDD_IPA_UC_NUM_WDI_PIPE == hdd_ipa->activated_fw_pipe) {
 			hdd_ipa->resource_loading = false;
+			complete(&hdd_ipa->ipa_resource_comp);
 			if (hdd_ipa->wdi_enabled == false) {
 				hdd_ipa->wdi_enabled = true;
 				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);
 		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);
 			if (hdd_ipa_is_rm_enabled(hdd_ipa->hdd_ctx))
 				ipa_rm_release_resource(
 					IPA_RM_RESOURCE_WLAN_PROD);
-			/*
-			 * Sync return success from IPA
-			 * Enable/resume all the PIPEs
-			 */
 			hdd_ipa->resource_unloading = false;
+			complete(&hdd_ipa->ipa_resource_comp);
 			hdd_ipa_uc_proc_pending_event(hdd_ipa);
 			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_set_quota_comp);
 		hdd_ipa_uc_sharing_stats_request(adapter,
 					     wdi_sap_stats->reset_stats);
 		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;
 		}
 
+		INIT_COMPLETION(hdd_ipa->ipa_uc_set_quota_comp);
 		hdd_ipa_uc_set_quota(adapter, ipa_set_quota->set_quota,
 				     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.
-	 * Store the events separately and handle them later.
 	 */
-	if (hdd_ipa_uc_is_enabled(hdd_ipa->hdd_ctx)) {
-		if (hdd_ipa->resource_loading) {
-			unsigned int pending_event_count;
-			struct ipa_uc_pending_event *pending_event = NULL;
+	if (hdd_ipa_uc_is_enabled(hdd_ipa->hdd_ctx) &&
+	    (hdd_ipa->resource_loading || hdd_ipa->resource_unloading)) {
+		unsigned int pending_event_count;
+		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,
-				    "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);
-			if (pending_event_count >=
-				HDD_IPA_MAX_PENDING_EVENT_COUNT) {
-				hdd_debug("Reached max pending event count");
-				qdf_list_remove_front(&hdd_ipa->pending_event,
-					(qdf_list_node_t **)&pending_event);
-			} else {
-				pending_event =
-					qdf_mem_malloc(sizeof(*pending_event));
-			}
+				if (pending_event_count >=
+				    HDD_IPA_MAX_PENDING_EVENT_COUNT) {
+					hdd_debug(
+					    "Reached max pending event count");
+					qdf_list_remove_front(
+					    &hdd_ipa->pending_event,
+					    (qdf_list_node_t **)&pending_event);
+				} else {
+					pending_event =
+						(struct ipa_uc_pending_event *)
+						qdf_mem_malloc(sizeof(
+						struct ipa_uc_pending_event));
+				}
 
-			if (!pending_event) {
-				qdf_mutex_release(&hdd_ipa->ipa_lock);
-				HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
-				    "Pending event memory alloc fail");
-				return -ENOMEM;
-			}
+				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);
+				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);
+				qdf_mutex_release(&hdd_ipa->ipa_lock);
+			}
 			return 0;
-		} else if (hdd_ipa->resource_unloading) {
+		} else {
 			HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
-				    "IPA resource unload in progress");
-			return 0;
+				    "IPA resource %s completed",
+				    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();
 	return QDF_STATUS_SUCCESS;