Эх сурвалжийг харах

qcacmn: split scheduler init/deinit

A commonly used and robust life-cycle pattern consists of the following
4 steps:

	1) create (allocate resources)
	2) start (mark resources as safe for use)
	3) stop (mark resources as unsafe for use)
	4) destroy (deallocate resources)

This pattern effectively prevents access to uninitialized and freed
resources. While the dispatcher psoc life-cycle follows this pattern,
the global life-cycle is condensed into steps 1 and 4. Unsurprisingly,
this led to uninitialized and freed resource access problems, especially
in regards to the scheduler thread.

Split the scheduler init and deinit life-cycle functions into init,
enable, disable, and deinit. Create new global enable and disable
dispatcher functions, and call the new scheduler APIs as appropriate.
This brings the global dispatcher life-cycle in line with the existing
psoc life-cycle, and prevents many scheduler related resource access
issues.

Change-Id: I58b65be0611f48e48354f28c221185f6f490f30c
CRs-Fixed: 2153283
Dustin Brown 7 жил өмнө
parent
commit
e0c9f6699f

+ 7 - 10
init_deinit/dispatcher/src/dispatcher_init_deinit.c

@@ -684,11 +684,6 @@ QDF_STATUS dispatcher_init(void)
 	if (QDF_STATUS_SUCCESS != dispatcher_splitmac_init())
 		goto splitmac_init_fail;
 
-	/*
-	 * scheduler INIT has to be the last as each component's
-	 * initialization has to happen first and then at the end
-	 * scheduler needs to start accepting the service.
-	 */
 	if (QDF_STATUS_SUCCESS != scheduler_init())
 		goto scheduler_init_fail;
 
@@ -736,10 +731,6 @@ EXPORT_SYMBOL(dispatcher_init);
 
 QDF_STATUS dispatcher_deinit(void)
 {
-	/*
-	 * schduler service should be the first one to stop offering
-	 * services up on dispatcher deinit sequence
-	 */
 	QDF_BUG(QDF_STATUS_SUCCESS == scheduler_deinit());
 
 	QDF_BUG(QDF_STATUS_SUCCESS == dispatcher_splitmac_deinit());
@@ -782,12 +773,18 @@ EXPORT_SYMBOL(dispatcher_deinit);
 
 QDF_STATUS dispatcher_enable(void)
 {
-	return QDF_STATUS_SUCCESS;
+	QDF_STATUS status;
+
+	status = scheduler_enable();
+
+	return status;
 }
 EXPORT_SYMBOL(dispatcher_enable);
 
 QDF_STATUS dispatcher_disable(void)
 {
+	QDF_BUG(QDF_IS_STATUS_SUCCESS(scheduler_disable()));
+
 	return QDF_STATUS_SUCCESS;
 }
 EXPORT_SYMBOL(dispatcher_disable);

+ 21 - 0
scheduler/inc/scheduler_api.h

@@ -106,6 +106,27 @@ QDF_STATUS scheduler_init(void);
  */
 QDF_STATUS scheduler_deinit(void);
 
+/**
+ * scheduler_enable() - start the scheduler module
+ *
+ * Ready the scheduler module to service requests, and start the scheduler's
+ * message processing thread. Must only be called after scheduler_init().
+ *
+ * Return: QDF_STATUS
+ */
+QDF_STATUS scheduler_enable(void);
+
+/**
+ * scheduler_disable() - stop the scheduler module
+ *
+ * Stop the scheduler module from servicing requests, and terminate the
+ * scheduler's message processing thread. Must be called before
+ * scheduler_deinit().
+ *
+ * Return: QDF_STATUS
+ */
+QDF_STATUS scheduler_disable(void);
+
 /**
  * scheduler_register_module() - register input module/queue id
  * @qid: queue id to get registered

+ 70 - 70
scheduler/src/scheduler_api.c

@@ -51,10 +51,13 @@ static void scheduler_flush_mqs(struct scheduler_ctx *sched_ctx)
 		scheduler_cleanup_queues(sched_ctx, i);
 }
 
-static QDF_STATUS scheduler_close(struct scheduler_ctx *sched_ctx)
+QDF_STATUS scheduler_disable(void)
 {
-	sched_info("Closing Scheduler");
+	struct scheduler_ctx *sched_ctx;
+
+	sched_info("Disabling Scheduler");
 
+	sched_ctx = scheduler_get_context();
 	QDF_BUG(sched_ctx);
 	if (!sched_ctx) {
 		sched_err("sched_ctx is NULL");
@@ -73,12 +76,6 @@ static QDF_STATUS scheduler_close(struct scheduler_ctx *sched_ctx)
 	/* flush any unprocessed scheduler messages */
 	scheduler_flush_mqs(sched_ctx);
 
-	qdf_timer_free(&sched_ctx->watchdog_timer);
-	qdf_spinlock_destroy(&sched_ctx->sch_thread_lock);
-	qdf_event_destroy(&sched_ctx->resume_sch_event);
-	qdf_event_destroy(&sched_ctx->sch_shutdown);
-	qdf_event_destroy(&sched_ctx->sch_start_event);
-
 	return QDF_STATUS_SUCCESS;
 }
 
@@ -117,53 +114,30 @@ static void scheduler_watchdog_timeout(void *arg)
 }
 #endif
 
-static QDF_STATUS scheduler_open(struct scheduler_ctx *sched_ctx)
+QDF_STATUS scheduler_enable(void)
 {
-	QDF_STATUS status;
+	struct scheduler_ctx *sched_ctx;
 
-	sched_info("Opening Scheduler");
+	sched_info("Enabling Scheduler");
 
-	/* Sanity checks */
+	sched_ctx = scheduler_get_context();
 	QDF_BUG(sched_ctx);
 	if (!sched_ctx) {
 		sched_err("sched_ctx is null");
 		return QDF_STATUS_E_INVAL;
 	}
 
-	status = qdf_event_create(&sched_ctx->sch_start_event);
-	if (QDF_IS_STATUS_ERROR(status)) {
-		sched_err("Failed to create start event; status:%d", status);
-		return status;
-	}
-
-	status = qdf_event_create(&sched_ctx->sch_shutdown);
-	if (QDF_IS_STATUS_ERROR(status)) {
-		sched_err("Failed to create shutdown event; status:%d", status);
-		goto start_event_destroy;
-	}
-
-	status = qdf_event_create(&sched_ctx->resume_sch_event);
-	if (QDF_IS_STATUS_ERROR(status)) {
-		sched_err("Failed to create resume event; status:%d", status);
-		goto shutdown_event_destroy;
-	}
-
-	qdf_spinlock_create(&sched_ctx->sch_thread_lock);
-	qdf_init_waitqueue_head(&sched_ctx->sch_wait_queue);
-	sched_ctx->sch_event_flag = 0;
-	qdf_timer_init(NULL,
-		       &sched_ctx->watchdog_timer,
-		       &scheduler_watchdog_timeout,
-		       sched_ctx,
-		       QDF_TIMER_TYPE_SW);
+	qdf_atomic_clear_bit(MC_SHUTDOWN_EVENT_MASK,
+			     &sched_ctx->sch_event_flag);
+	qdf_atomic_clear_bit(MC_POST_EVENT_MASK,
+			     &sched_ctx->sch_event_flag);
 
 	/* create the scheduler thread */
-	sched_ctx->sch_thread = qdf_create_thread(scheduler_thread,
-					sched_ctx, "scheduler_thread");
+	sched_ctx->sch_thread = qdf_create_thread(scheduler_thread, sched_ctx,
+						  "scheduler_thread");
 	if (IS_ERR(sched_ctx->sch_thread)) {
 		sched_err("Failed to create scheduler thread");
-		status = QDF_STATUS_E_RESOURCES;
-		goto wd_timer_destroy;
+		return QDF_STATUS_E_RESOURCES;
 	}
 
 	sched_info("Scheduler thread created");
@@ -175,31 +149,18 @@ static QDF_STATUS scheduler_open(struct scheduler_ctx *sched_ctx)
 	sched_info("Scheduler thread started");
 
 	return QDF_STATUS_SUCCESS;
-
-wd_timer_destroy:
-	qdf_timer_free(&sched_ctx->watchdog_timer);
-	qdf_spinlock_destroy(&sched_ctx->sch_thread_lock);
-	qdf_event_destroy(&sched_ctx->resume_sch_event);
-
-shutdown_event_destroy:
-	qdf_event_destroy(&sched_ctx->sch_shutdown);
-
-start_event_destroy:
-	qdf_event_destroy(&sched_ctx->sch_start_event);
-
-	return status;
 }
 
 QDF_STATUS scheduler_init(void)
 {
-	QDF_STATUS status = QDF_STATUS_SUCCESS;
+	QDF_STATUS status;
 	struct scheduler_ctx *sched_ctx;
 
 	sched_info("Initializing Scheduler");
 
 	status = scheduler_create_ctx();
-	if (QDF_STATUS_SUCCESS != status) {
-		sched_err("can't create scheduler ctx");
+	if (QDF_IS_STATUS_ERROR(status)) {
+		sched_err("Failed to create context; status:%d", status);
 		return status;
 	}
 
@@ -213,20 +174,47 @@ QDF_STATUS scheduler_init(void)
 
 	status = scheduler_queues_init(sched_ctx);
 	if (QDF_IS_STATUS_ERROR(status)) {
-		sched_err("Queue init failed");
+		sched_err("Failed to init queues; status:%d", status);
 		goto ctx_destroy;
 	}
 
-	status = scheduler_open(sched_ctx);
+	status = qdf_event_create(&sched_ctx->sch_start_event);
 	if (QDF_IS_STATUS_ERROR(status)) {
-		sched_err("Failed to open QDF Scheduler");
+		sched_err("Failed to create start event; status:%d", status);
 		goto queues_deinit;
 	}
 
+	status = qdf_event_create(&sched_ctx->sch_shutdown);
+	if (QDF_IS_STATUS_ERROR(status)) {
+		sched_err("Failed to create shutdown event; status:%d", status);
+		goto start_event_destroy;
+	}
+
+	status = qdf_event_create(&sched_ctx->resume_sch_event);
+	if (QDF_IS_STATUS_ERROR(status)) {
+		sched_err("Failed to create resume event; status:%d", status);
+		goto shutdown_event_destroy;
+	}
+
+	qdf_spinlock_create(&sched_ctx->sch_thread_lock);
+	qdf_init_waitqueue_head(&sched_ctx->sch_wait_queue);
+	sched_ctx->sch_event_flag = 0;
+	qdf_timer_init(NULL,
+		       &sched_ctx->watchdog_timer,
+		       &scheduler_watchdog_timeout,
+		       sched_ctx,
+		       QDF_TIMER_TYPE_SW);
+
 	qdf_register_mc_timer_callback(scheduler_mc_timer_callback);
 
 	return QDF_STATUS_SUCCESS;
 
+shutdown_event_destroy:
+	qdf_event_destroy(&sched_ctx->sch_shutdown);
+
+start_event_destroy:
+	qdf_event_destroy(&sched_ctx->sch_start_event);
+
 queues_deinit:
 	scheduler_queues_deinit(sched_ctx);
 
@@ -238,22 +226,34 @@ ctx_destroy:
 
 QDF_STATUS scheduler_deinit(void)
 {
-	QDF_STATUS status = QDF_STATUS_SUCCESS;
-	struct scheduler_ctx *sched_ctx = scheduler_get_context();
+	QDF_STATUS status;
+	struct scheduler_ctx *sched_ctx;
 
 	sched_info("Deinitializing Scheduler");
 
-	status = scheduler_close(sched_ctx);
-	if (QDF_STATUS_SUCCESS != status) {
-		sched_err("Scheduler close failed");
-		return status;
+	sched_ctx = scheduler_get_context();
+	QDF_BUG(sched_ctx);
+	if (!sched_ctx) {
+		sched_err("sched_ctx is null");
+		return QDF_STATUS_E_INVAL;
 	}
 
-	scheduler_queues_deinit(sched_ctx);
+	qdf_timer_free(&sched_ctx->watchdog_timer);
+	qdf_spinlock_destroy(&sched_ctx->sch_thread_lock);
+	qdf_event_destroy(&sched_ctx->resume_sch_event);
+	qdf_event_destroy(&sched_ctx->sch_shutdown);
+	qdf_event_destroy(&sched_ctx->sch_start_event);
 
-	return scheduler_destroy_ctx();
-}
+	status = scheduler_queues_deinit(sched_ctx);
+	if (QDF_IS_STATUS_ERROR(status))
+		sched_err("Failed to deinit queues; status:%d", status);
 
+	status = scheduler_destroy_ctx();
+	if (QDF_IS_STATUS_ERROR(status))
+		sched_err("Failed to destroy context; status:%d", status);
+
+	return QDF_STATUS_SUCCESS;
+}
 
 QDF_STATUS scheduler_post_msg_by_priority(QDF_MODULE_ID qid,
 		struct scheduler_msg *pMsg, bool is_high_priority)