瀏覽代碼

qcacld-3.0: Fix race condition for bandwidth timer start/restart

The MC thread uses a timer to measure bandwidth usage in station and
soft AP modes. This timer restarts itself periodically, which can
race with the MC thread during a stop/start cycle. Fix this race
condition by introducing a spinlock that protects the timer during
start and restart.

Change-Id: I0e86687d28fb07a0a41c9475b89f5f170e72bf77
CRs-Fixed: 1112498
Dustin Brown 8 年之前
父節點
當前提交
fce08d1a4a
共有 2 個文件被更改,包括 28 次插入12 次删除
  1. 3 2
      core/hdd/inc/wlan_hdd_main.h
  2. 25 10
      core/hdd/src/wlan_hdd_main.c

+ 3 - 2
core/hdd/inc/wlan_hdd_main.h

@@ -1445,8 +1445,9 @@ struct hdd_context_s {
 	/* DDR bus bandwidth compute timer
 	 */
 	qdf_timer_t bus_bw_timer;
-	bool bus_bw_timer_started;
-	struct work_struct  bus_bw_work;
+	bool bus_bw_timer_running;
+	qdf_spinlock_t bus_bw_timer_lock;
+	struct work_struct bus_bw_work;
 	int cur_vote_level;
 	spinlock_t bus_bw_lock;
 	int cur_rx_level;

+ 25 - 10
core/hdd/src/wlan_hdd_main.c

@@ -5522,8 +5522,14 @@ static void hdd_bus_bw_work_handler(struct work_struct *work)
 	hdd_ipa_set_perf_level(hdd_ctx, tx_packets, rx_packets);
 	hdd_ipa_uc_stat_request(adapter, 2);
 
-	qdf_timer_start(&hdd_ctx->bus_bw_timer,
-			   hdd_ctx->config->busBandwidthComputeInterval);
+	/* restart the periodic timer */
+	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);
 }
 
 /**
@@ -5562,6 +5568,8 @@ int hdd_bus_bandwidth_init(hdd_context_t *hdd_ctx)
 	spin_lock_init(&hdd_ctx->bus_bw_lock);
 	INIT_WORK(&hdd_ctx->bus_bw_work,
 			hdd_bus_bw_work_handler);
+	hdd_ctx->bus_bw_timer_running = false;
+	qdf_spinlock_create(&hdd_ctx->bus_bw_timer_lock);
 	qdf_timer_init(NULL,
 		 &hdd_ctx->bus_bw_timer,
 		 hdd_bus_bw_cbk, (void *)hdd_ctx,
@@ -5572,12 +5580,14 @@ int hdd_bus_bandwidth_init(hdd_context_t *hdd_ctx)
 
 void hdd_bus_bandwidth_destroy(hdd_context_t *hdd_ctx)
 {
-	if (hdd_ctx->bus_bw_timer_started)
+	if (hdd_ctx->bus_bw_timer_running)
 		hdd_reset_tcp_delack(hdd_ctx);
 
 	hdd_info("wait for bus bw work to flush");
 	cancel_work_sync(&hdd_ctx->bus_bw_work);
 	qdf_timer_free(&hdd_ctx->bus_bw_timer);
+	hdd_ctx->bus_bw_timer_running = false;
+	qdf_spinlock_destroy(&hdd_ctx->bus_bw_timer_lock);
 }
 #endif
 
@@ -8917,12 +8927,15 @@ void hdd_start_bus_bw_compute_timer(hdd_adapter_t *adapter)
 {
 	hdd_context_t *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
 
-	if (hdd_ctx->bus_bw_timer_started)
-		return;
+	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);
 
-	hdd_ctx->bus_bw_timer_started = true;
-	qdf_timer_start(&hdd_ctx->bus_bw_timer,
-			   hdd_ctx->config->busBandwidthComputeInterval);
 }
 
 void hdd_stop_bus_bw_compute_timer(hdd_adapter_t *adapter)
@@ -8932,7 +8945,7 @@ void hdd_stop_bus_bw_compute_timer(hdd_adapter_t *adapter)
 	bool can_stop = true;
 	hdd_context_t *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
 
-	if (!hdd_ctx->bus_bw_timer_started) {
+	if (!hdd_ctx->bus_bw_timer_running) {
 		/* trying to stop timer, when not running is not good */
 		hdd_info("bus band width compute timer is not running");
 		return;
@@ -8970,8 +8983,10 @@ void hdd_stop_bus_bw_compute_timer(hdd_adapter_t *adapter)
 	if (can_stop == true) {
 		/* reset the ipa perf level */
 		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_started = false;
+		hdd_ctx->bus_bw_timer_running = false;
+		qdf_spinlock_release(&hdd_ctx->bus_bw_timer_lock);
 		hdd_reset_tcp_delack(hdd_ctx);
 	}
 }