[Skiboot] [PATCH v2 7/9] Add global CPU job queue

Stewart Smith stewart at linux.vnet.ibm.com
Wed May 6 14:00:52 AEST 2015


When we have multiple systems trying to start concurrent jobs on different
CPUs, they typically pick the first available (operating) CPU to schedule
the job on. This works fine when there's only one set of jobs or when we
want to bind jobs to specific CPUs.

When we have jobs such as asynchronously loading LIDs and scanning PHBs,
we don't care which CPUs they run on, we care more that they are not
scheduled on CPUs that have existing tasks.

This patch adds a global queue of jobs which secondary CPUs will look
at for work (if idle).

This leads to simplified callers, which just need to queue jobs to NULL
(no specific CPU) and then call a magic function that will run the
CPU job queue if we don't have secondary CPUs.

Additionally, we add a const char *name to cpu_job just to aid with
debugging.

Signed-off-by: Stewart Smith <stewart at linux.vnet.ibm.com>
---
 core/cpu.c    |   65 ++++++++++++++++++++++++++++++++++++++++++++++++---------
 core/pci.c    |   32 +++++-----------------------
 hw/chiptod.c  |    9 +++++---
 hw/slw.c      |    3 ++-
 include/cpu.h |    6 +++++-
 5 files changed, 73 insertions(+), 42 deletions(-)

diff --git a/core/cpu.c b/core/cpu.c
index 262316c..9eb4240 100644
--- a/core/cpu.c
+++ b/core/cpu.c
@@ -54,10 +54,14 @@ struct cpu_job {
 	struct list_node	link;
 	void			(*func)(void *data);
 	void			*data;
+	const char		*name;
 	bool			complete;
 	bool		        no_return;
 };
 
+static struct lock global_job_queue_lock = LOCK_UNLOCKED;
+static struct list_head	global_job_queue;
+
 /* attribute const as cpu_stacks is constant. */
 unsigned long __attrconst cpu_stack_bottom(unsigned int pir)
 {
@@ -89,12 +93,13 @@ void __nomcount cpu_relax(void)
 }
 
 struct cpu_job *__cpu_queue_job(struct cpu_thread *cpu,
+				const char *name,
 				void (*func)(void *data), void *data,
 				bool no_return)
 {
 	struct cpu_job *job;
 
-	if (!cpu_is_available(cpu)) {
+	if (cpu && !cpu_is_available(cpu)) {
 		prerror("CPU: Tried to queue job on unavailable CPU 0x%04x\n",
 			cpu->pir);
 		return NULL;
@@ -105,10 +110,15 @@ struct cpu_job *__cpu_queue_job(struct cpu_thread *cpu,
 		return NULL;
 	job->func = func;
 	job->data = data;
+	job->name = name;
 	job->complete = false;
 	job->no_return = no_return;
 
-	if (cpu != this_cpu()) {
+	if (cpu == NULL) {
+		lock(&global_job_queue_lock);
+		list_add_tail(&global_job_queue, &job->link);
+		unlock(&global_job_queue_lock);
+	} else if (cpu != this_cpu()) {
 		lock(&cpu->job_lock);
 		list_add_tail(&cpu->job_queue, &job->link);
 		unlock(&cpu->job_lock);
@@ -158,30 +168,40 @@ void cpu_free_job(struct cpu_job *job)
 void cpu_process_jobs(void)
 {
 	struct cpu_thread *cpu = this_cpu();
-	struct cpu_job *job;
+	struct cpu_job *job = NULL;
 	void (*func)(void *);
 	void *data;
 
 	sync();
-	if (list_empty(&cpu->job_queue))
+	if (list_empty(&cpu->job_queue) && list_empty(&global_job_queue))
 		return;
 
 	lock(&cpu->job_lock);
 	while (true) {
 		bool no_return;
 
-		if (list_empty(&cpu->job_queue))
-			break;
-		smt_medium();
-		job = list_pop(&cpu->job_queue, struct cpu_job, link);
+		if (list_empty(&cpu->job_queue)) {
+			smt_medium();
+			if (list_empty(&global_job_queue))
+				break;
+			lock(&global_job_queue_lock);
+			job = list_pop(&global_job_queue, struct cpu_job, link);
+			unlock(&global_job_queue_lock);
+		} else {
+			smt_medium();
+			job = list_pop(&cpu->job_queue, struct cpu_job, link);
+		}
+
 		if (!job)
 			break;
+
 		func = job->func;
 		data = job->data;
 		no_return = job->no_return;
 		unlock(&cpu->job_lock);
 		if (no_return)
 			free(job);
+		prlog(PR_TRACE, "running job %s on %x\n", job->name, cpu->pir);
 		func(data);
 		lock(&cpu->job_lock);
 		if (!no_return) {
@@ -192,6 +212,27 @@ void cpu_process_jobs(void)
 	unlock(&cpu->job_lock);
 }
 
+void cpu_process_local_jobs(void)
+{
+	struct cpu_thread *cpu = first_available_cpu();
+
+	while (cpu) {
+		if (cpu != this_cpu())
+			break;
+
+		cpu = next_available_cpu(cpu);
+		if (!cpu)
+			cpu = first_available_cpu();
+
+		/* No CPU to run on, just run synchro */
+		if (cpu == this_cpu()) {
+			printf("Processing jobs synchronously\n");
+			cpu_process_jobs();
+		}
+	}
+}
+
+
 struct dt_node *get_cpu_node(u32 pir)
 {
 	struct cpu_thread *t = find_cpu_by_pir(pir);
@@ -413,6 +454,8 @@ void init_boot_cpu(void)
 	init_cpu_thread(boot_cpu, cpu_state_active, pir);
 	init_boot_tracebuf(boot_cpu);
 	assert(this_cpu() == boot_cpu);
+
+	list_head_init(&global_job_queue);
 }
 
 void init_all_cpus(void)
@@ -568,7 +611,8 @@ static int64_t opal_start_cpu_thread(uint64_t server_no, uint64_t start_address)
 		prerror("OPAL: CPU not active in OPAL !\n");
 		return OPAL_WRONG_STATE;
 	}
-	job = __cpu_queue_job(cpu, opal_start_thread_job, (void *)start_address,
+	job = __cpu_queue_job(cpu, "start_thread",
+			      opal_start_thread_job, (void *)start_address,
 			      true);
 	unlock(&reinit_lock);
 	if (!job) {
@@ -648,7 +692,8 @@ static int64_t cpu_change_all_hile(bool hile)
 			cpu_change_hile(&hile);
 			continue;
 		}
-		cpu_wait_job(cpu_queue_job(cpu, cpu_change_hile, &hile), true);
+		cpu_wait_job(cpu_queue_job(cpu, "cpu_change_hile",
+					   cpu_change_hile, &hile), true);
 	}
 	return OPAL_SUCCESS;
 }
diff --git a/core/pci.c b/core/pci.c
index 8ea9177..7447cdf 100644
--- a/core/pci.c
+++ b/core/pci.c
@@ -1382,7 +1382,6 @@ void pci_reset(void)
 
 static void pci_do_jobs(void (*fn)(void *))
 {
-	struct cpu_thread *cpu = first_available_cpu();
 	void *jobs[ARRAY_SIZE(phbs)];
 	int i;
 
@@ -1392,36 +1391,15 @@ static void pci_do_jobs(void (*fn)(void *))
 			continue;
 		}
 
-		/* Don't queue job to current CPU, which is always
-		 * the poller.
-		 */
-		while (cpu) {
-			if (cpu != this_cpu())
-				break;
-
-			cpu = next_available_cpu(cpu);
-			if (!cpu)
-				cpu = first_available_cpu();
-
-			/* No CPU to run on, just run synchro */
-			if (cpu == this_cpu()) {
-				fn(phbs[i]);
-				jobs[i] = NULL;
-				goto next_phb;
-			}
-		}
-
-		jobs[i] = __cpu_queue_job(cpu, fn, phbs[i], false);
+		jobs[i] = __cpu_queue_job(NULL, phbs[i]->dt_node->name,
+					  fn, phbs[i], false);
 		assert(jobs[i]);
 
-		/* For next CPU */
-		cpu = next_available_cpu(cpu);
-		if (!cpu)
-			cpu = first_available_cpu();
-	next_phb:
-		;
 	}
 
+	/* If no secondary CPUs, do everything sync */
+	cpu_process_local_jobs();
+
 	/* Wait until all tasks are done */
 	for (i = 0; i < ARRAY_SIZE(phbs); i++) {
 		if (!jobs[i])
diff --git a/hw/chiptod.c b/hw/chiptod.c
index e5c3a22..18c4847 100644
--- a/hw/chiptod.c
+++ b/hw/chiptod.c
@@ -970,7 +970,8 @@ void chiptod_init(void)
 
 	/* Schedule master sync */
 	sres = false;
-	cpu_wait_job(cpu_queue_job(cpu0, chiptod_sync_master, &sres), true);
+	cpu_wait_job(cpu_queue_job(cpu0, "chiptod_sync_master",
+				   chiptod_sync_master, &sres), true);
 	if (!sres) {
 		op_display(OP_FATAL, OP_MOD_CHIPTOD, 2);
 		abort();
@@ -986,7 +987,8 @@ void chiptod_init(void)
 
 		/* Queue job */
 		sres = false;
-		cpu_wait_job(cpu_queue_job(cpu, chiptod_sync_slave, &sres),
+		cpu_wait_job(cpu_queue_job(cpu, "chiptod_sync_slave",
+					   chiptod_sync_slave, &sres),
 			     true);
 		if (!sres) {
 			op_display(OP_WARN, OP_MOD_CHIPTOD, 3|(cpu->pir << 8));
@@ -1002,7 +1004,8 @@ void chiptod_init(void)
 		/* Only do primaries, not threads */
 		if (cpu->is_secondary)
 			continue;
-		cpu_wait_job(cpu_queue_job(cpu, chiptod_print_tb, NULL), true);
+		cpu_wait_job(cpu_queue_job(cpu, "chiptod_print_tb",
+					   chiptod_print_tb, NULL), true);
 	}
 
 	op_display(OP_LOG, OP_MOD_CHIPTOD, 4);
diff --git a/hw/slw.c b/hw/slw.c
index 458560c..ffa9de1 100644
--- a/hw/slw.c
+++ b/hw/slw.c
@@ -792,7 +792,8 @@ int64_t slw_reinit(uint64_t flags)
 			has_waker = true;
 			master = this_cpu();
 		}
-		__cpu_queue_job(cpu, slw_do_rvwinkle, master, true);
+		__cpu_queue_job(cpu, "slw_do_rvwinkle",
+				slw_do_rvwinkle, master, true);
 
 		/* Wait for it to claim to be down */
 		while(cpu->state != cpu_state_rvwinkle)
diff --git a/include/cpu.h b/include/cpu.h
index 168714a..2bf15ea 100644
--- a/include/cpu.h
+++ b/include/cpu.h
@@ -197,14 +197,16 @@ void cpu_disable_all_threads(struct cpu_thread *cpu);
 
 /* Allocate & queue a job on target CPU */
 extern struct cpu_job *__cpu_queue_job(struct cpu_thread *cpu,
+				       const char *name,
 				       void (*func)(void *data), void *data,
 				       bool no_return);
 
 static inline struct cpu_job *cpu_queue_job(struct cpu_thread *cpu,
+					    const char *name,
 					    void (*func)(void *data),
 					    void *data)
 {
-	return __cpu_queue_job(cpu, func, data, false);
+	return __cpu_queue_job(cpu, name, func, data, false);
 }
 
 
@@ -222,6 +224,8 @@ extern void cpu_free_job(struct cpu_job *job);
 
 /* Called by init to process jobs */
 extern void cpu_process_jobs(void);
+/* Fallback to running jobs synchronously for global jobs */
+extern void cpu_process_local_jobs(void);
 
 static inline void cpu_give_self_os(void)
 {
-- 
1.7.10.4



More information about the Skiboot mailing list