Просмотр исходного кода

qcacld-3.0: Mechanism to complete waiting thread

During remove() or shutdown(), driver waits for external thread
to complete but there is no mechanism available today to inform
lower layers that driver is about to go through remove() or
shutdown().  Add mechanism to provide the notification about
remove() or shutdown() so that lower layers can make use of it
and get out of waiting logic and complete the remove() or
shutdown() early without waiting for the timeout to happen.

Change-Id: I1eece21fb63e6d5e0454659795a4dad011483c75
CRs-fixed: 1079299
(cherry picked from commit 305516248006141ef331c8c1b7a4b56999fa496d)
Prashanth Bhatta 8 лет назад
Родитель
Сommit
f7969a6c2d
3 измененных файлов с 163 добавлено и 3 удалено
  1. 29 0
      core/cds/inc/cds_sched.h
  2. 2 0
      core/cds/src/cds_api.c
  3. 132 3
      core/cds/src/cds_sched.c

+ 29 - 0
core/cds/inc/cds_sched.h

@@ -551,4 +551,33 @@ void cds_ssr_unprotect(const char *caller_func);
 bool cds_wait_for_external_threads_completion(const char *caller_func);
 int cds_get_gfp_flags(void);
 
+/**
+ * cds_shutdown_notifier_register() - Register for shutdown notification
+ * @cb          : Call back to be called
+ * @priv        : Private pointer to be passed back to call back
+ *
+ * During driver remove or shutdown (recovery), external threads might be stuck
+ * waiting on some event from firmware at lower layers. Remove or shutdown can't
+ * proceed till the thread completes to avoid any race condition. Call backs can
+ * be registered here to get early notification of remove or shutdown so that
+ * waiting thread can be unblocked and hence remove or shutdown can proceed
+ * further as waiting there may not make sense when FW may already have been
+ * down.
+ *
+ * Return: CDS status
+ */
+QDF_STATUS cds_shutdown_notifier_register(void (*cb)(void *priv), void *priv);
+
+/**
+ * cds_shutdown_notifier_purge() - Purge all the notifiers
+ *
+ * Shutdown notifiers are added to provide the early notification of remove or
+ * shutdown being initiated. Adding this API to purge all the registered call
+ * backs as they are not useful any more while all the lower layers are being
+ * shutdown.
+ *
+ * Return: None
+ */
+void cds_shutdown_notifier_purge(void);
+
 #endif /* #if !defined __CDS_SCHED_H */

+ 2 - 0
core/cds/src/cds_api.c

@@ -824,6 +824,8 @@ QDF_STATUS cds_close(v_CONTEXT_t cds_context)
 		QDF_ASSERT(QDF_IS_STATUS_SUCCESS(qdf_status));
 	}
 
+	cds_shutdown_notifier_purge();
+
 	cds_deinit_log_completion();
 	cds_deinit_ini_config();
 	qdf_timer_module_deinit();

+ 132 - 3
core/cds/src/cds_sched.c

@@ -73,6 +73,20 @@ struct ssr_protect {
 static spinlock_t ssr_protect_lock;
 static struct ssr_protect ssr_protect_log[MAX_SSR_PROTECT_LOG];
 
+struct shutdown_notifier {
+	struct list_head list;
+	void (*cb)(void *priv);
+	void *priv;
+};
+
+struct list_head shutdown_notifier_head;
+
+enum notifier_state {
+	NOTIFIER_STATE_NONE,
+	NOTIFIER_STATE_NOTIFYING,
+} notifier_state;
+
+
 static p_cds_sched_context gp_cds_sched_context;
 
 static int cds_mc_thread(void *Arg);
@@ -1373,6 +1387,8 @@ void cds_ssr_protect_init(void)
 		ssr_protect_log[i].pid =  0;
 		i++;
 	}
+
+	INIT_LIST_HEAD(&shutdown_notifier_head);
 }
 
 /**
@@ -1491,6 +1507,113 @@ void cds_ssr_unprotect(const char *caller_func)
 			  caller_func, current->pid, count);
 }
 
+/**
+ * cds_shutdown_notifier_register() - Register for shutdown notification
+ * @cb          : Call back to be called
+ * @priv        : Private pointer to be passed back to call back
+ *
+ * During driver remove or shutdown (recovery), external threads might be stuck
+ * waiting on some event from firmware at lower layers. Remove or shutdown can't
+ * proceed till the thread completes to avoid any race condition. Call backs can
+ * be registered here to get early notification of remove or shutdown so that
+ * waiting thread can be unblocked and hence remove or shutdown can proceed
+ * further as waiting there may not make sense when FW may already have been
+ * down.
+ *
+ * This is intended for early notification of remove() or shutdown() only so
+ * that lower layers can take care of stuffs like external waiting thread.
+ *
+ * Return: CDS status
+ */
+QDF_STATUS cds_shutdown_notifier_register(void (*cb)(void *priv), void *priv)
+{
+	struct shutdown_notifier *notifier;
+	unsigned long irq_flags;
+
+	notifier = qdf_mem_malloc(sizeof(*notifier));
+
+	if (notifier == NULL)
+		return QDF_STATUS_E_NOMEM;
+
+	/*
+	 * This logic can be simpilfied if there is separate state maintained
+	 * for shutdown and reinit. Right now there is only recovery in progress
+	 * state and it doesn't help to check against it as during reinit some
+	 * of the modules may need to register the call backs.
+	 * For now this logic added to avoid notifier registration happen while
+	 * this function is trying to call the call back with the notification.
+	 */
+	spin_lock_irqsave(&ssr_protect_lock, irq_flags);
+	if (notifier_state == NOTIFIER_STATE_NOTIFYING) {
+		spin_unlock_irqrestore(&ssr_protect_lock, irq_flags);
+		qdf_mem_free(notifier);
+		return -EINVAL;
+	}
+
+	notifier->cb = cb;
+	notifier->priv = priv;
+
+	list_add_tail(&notifier->list, &shutdown_notifier_head);
+	spin_unlock_irqrestore(&ssr_protect_lock, irq_flags);
+
+	return 0;
+}
+
+/**
+ * cds_shutdown_notifier_purge() - Purge all the notifiers
+ *
+ * Shutdown notifiers are added to provide the early notification of remove or
+ * shutdown being initiated. Adding this API to purge all the registered call
+ * backs as they are not useful any more while all the lower layers are being
+ * shutdown.
+ *
+ * Return: None
+ */
+void cds_shutdown_notifier_purge(void)
+{
+	struct shutdown_notifier *notifier, *temp;
+	unsigned long irq_flags;
+
+	spin_lock_irqsave(&ssr_protect_lock, irq_flags);
+	list_for_each_entry_safe(notifier, temp,
+				 &shutdown_notifier_head, list) {
+		list_del(&notifier->list);
+		spin_unlock_irqrestore(&ssr_protect_lock, irq_flags);
+
+		qdf_mem_free(notifier);
+
+		spin_lock_irqsave(&ssr_protect_lock, irq_flags);
+	}
+
+	spin_unlock_irqrestore(&ssr_protect_lock, irq_flags);
+}
+
+/**
+ * cds_shutdown_notifier_call() - Call shutdown notifier call back
+ *
+ * Call registered shutdown notifier call back to indicate about remove or
+ * shutdown.
+ */
+static void cds_shutdown_notifier_call(void)
+{
+	struct shutdown_notifier *notifier;
+	unsigned long irq_flags;
+
+	spin_lock_irqsave(&ssr_protect_lock, irq_flags);
+	notifier_state = NOTIFIER_STATE_NOTIFYING;
+
+	list_for_each_entry(notifier, &shutdown_notifier_head, list) {
+		spin_unlock_irqrestore(&ssr_protect_lock, irq_flags);
+
+		notifier->cb(notifier->priv);
+
+		spin_lock_irqsave(&ssr_protect_lock, irq_flags);
+	}
+
+	notifier_state = NOTIFIER_STATE_NONE;
+	spin_unlock_irqrestore(&ssr_protect_lock, irq_flags);
+}
+
 /**
  * cds_wait_for_external_threads_completion() - wait for external threads
  *					completion before proceeding further
@@ -1502,19 +1625,25 @@ void cds_ssr_unprotect(const char *caller_func)
 bool cds_wait_for_external_threads_completion(const char *caller_func)
 {
 	int count = MAX_SSR_WAIT_ITERATIONS;
+	int r;
+
+	cds_shutdown_notifier_call();
 
 	while (count) {
 
-		if (!atomic_read(&ssr_protect_entry_count))
+		r = atomic_read(&ssr_protect_entry_count);
+
+		if (!r)
 			break;
 
 		if (--count) {
 			QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR,
-				  "%s: Waiting for active entry points to exit",
-				  __func__);
+				  "%s: Waiting for %d active entry points to exit",
+				  __func__, r);
 			msleep(SSR_WAIT_SLEEP_TIME);
 		}
 	}
+
 	/* at least one external thread is executing */
 	if (!count) {
 		QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR,