Browse Source

qcacmn: Add memory barrier to avoid inconsistent reg write for valid flag

Add memory barrier to avoid inconsistent reg write for valid flag.

Change-Id: Ieb4ed80872961889f29de083a6b1dcdbe6a303d2
CRs-Fixed: 2699549
Tiger Yu 5 years ago
parent
commit
0f08390fa4
3 changed files with 26 additions and 1 deletions
  1. 2 0
      hal/wifi3.0/hal_internal.h
  2. 19 1
      hal/wifi3.0/hal_srng.c
  3. 5 0
      qdf/inc/qdf_util.h

+ 2 - 0
hal/wifi3.0/hal_internal.h

@@ -237,6 +237,7 @@ typedef struct hal_ring_handle *hal_ring_handle_t;
  * @dequeue_val: register value at the time of delayed write dequeue
  * @valid: whether this entry is valid or not
  * @enqueue_time: enqueue time (qdf_log_timestamp)
+ * @work_scheduled_time: work scheduled time (qdf_log_timestamp)
  * @dequeue_time: dequeue time (qdf_log_timestamp)
  */
 struct hal_reg_write_q_elem {
@@ -246,6 +247,7 @@ struct hal_reg_write_q_elem {
 	uint32_t dequeue_val;
 	uint8_t valid;
 	qdf_time_t enqueue_time;
+	qdf_time_t work_scheduled_time;
 	qdf_time_t dequeue_time;
 };
 

+ 19 - 1
hal/wifi3.0/hal_srng.c

@@ -461,7 +461,10 @@ static void hal_reg_write_work(void *arg)
 	uint32_t *addr;
 
 	q_elem = &hal->reg_write_queue[(hal->read_idx)];
+	q_elem->work_scheduled_time = qdf_get_log_timestamp();
 
+	/* Make sure q_elem consistent in the memory for multi-cores */
+	qdf_rmb();
 	if (!q_elem->valid)
 		return;
 
@@ -474,7 +477,11 @@ static void hal_reg_write_work(void *arg)
 		return;
 	}
 
-	while (q_elem->valid) {
+	while (true) {
+		qdf_rmb();
+		if (!q_elem->valid)
+			break;
+
 		q_elem->dequeue_time = qdf_get_log_timestamp();
 		ring_id = q_elem->srng->ring_id;
 		addr = q_elem->addr;
@@ -569,6 +576,17 @@ static void hal_reg_write_enqueue(struct hal_soc *hal_soc,
 	qdf_wmb();
 	q_elem->valid = true;
 
+	/*
+	 * After all other fields in the q_elem has been updated
+	 * in memory successfully, the valid flag needs to be updated
+	 * in memory in time too.
+	 * Else there is a chance that the dequeuing worker thread
+	 * might read stale valid flag and the work will be bypassed
+	 * for this round. And if there is no other work scheduled
+	 * later, this hal register writing won't be updated any more.
+	 */
+	qdf_wmb();
+
 	srng->reg_write_in_progress  = true;
 	qdf_atomic_inc(&hal_soc->active_work_cnt);
 

+ 5 - 0
qdf/inc/qdf_util.h

@@ -51,6 +51,11 @@ typedef __qdf_wait_queue_head_t qdf_wait_queue_head_t;
  */
 #define qdf_wmb()                 __qdf_wmb()
 
+/**
+ * qdf_rmb - read memory barrier.
+ */
+#define qdf_rmb()                 __qdf_rmb()
+
 /**
  * qdf_mb - read + write memory barrier.
  */