This defines just the API, nothing is changed in the scheduler. I'd like to
get feedback on the choices I made.
Unfortunately, since in GNU/Hurd thread creation pass through the Task API,
and not the thread API, affinity inheritance will have to be implemented in
glibc.
---
include/mach/gnumach.defs | 8 ++
kern/thread.c | 159 ++++++++++++++++++++++++++++++++++++++
kern/thread.h | 45 +++++++++++
3 files changed, 212 insertions(+)
diff --git a/include/mach/gnumach.defs b/include/mach/gnumach.defs
index f5b2f7f2..9f98b47b 100644
--- a/include/mach/gnumach.defs
+++ b/include/mach/gnumach.defs
@@ -257,3 +257,11 @@ routine vm_get_size_limit(
map : vm_task_t;
out current_limit : vm_size_t;
out max_limit : vm_size_t);
+
+routine thread_set_affinity(
+ thread : thread_t;
+ processors_list : processor_name_array_t);
+
+routine thread_get_affinity(
+ thread : thread_t;
+ out processors_list : processor_name_array_t);
diff --git a/kern/thread.c b/kern/thread.c
index deb9688d..fb5e2a7e 100644
--- a/kern/thread.c
+++ b/kern/thread.c
@@ -367,6 +367,8 @@ void thread_init(void)
/* thread_template.processor_set (later) */
thread_template.bound_processor = PROCESSOR_NULL;
+ /* thread_template.affinity_set = FALSE; */
+ /* cpu_affinity_zero(&thread_template.affinity); */
#if MACH_HOST
thread_template.may_assign = TRUE;
thread_template.assign_active = FALSE;
@@ -451,6 +453,11 @@ kern_return_t thread_create(
pset_reference(pset);
task_unlock(parent_task);
+ /*
+ * Reset affinity
+ */
+ new_thread->affinity_set = FALSE;
+
/*
* This thread will mosty probably start working, assume it
* will take its share of CPU, to avoid having to find it out
@@ -2672,3 +2679,155 @@ thread_get_name(
return KERN_SUCCESS;
}
+
+/*
+ *
+ * thread_set_affinity
+ *
+ * Set thread affinity, the procs array contains the target CPUs.
+ * A NULL array indicates to clear affinity and set default.
+ * Returns an error if any CPU is outside of the thread processor set.
+ *
+ */
+kern_return_t
+thread_set_affinity(
+ thread_t thread,
+ processor_name_array_t processor_list,
+ natural_t countp)
+{
+ cpu_affinity_t newmask;
+ processor_t p;
+ int i;
+ boolean_t any = FALSE;
+ spl_t s;
+
+ if (thread == THREAD_NULL)
+ return KERN_INVALID_ARGUMENT;
+
+ if (processor_list == NULL) {
+ s = splsched();
+ thread_lock(thread);
+ thread->affinity_set = FALSE;
+ cpu_affinity_zero(&thread->affinity);
+ thread_unlock(thread);
+ splx(s);
+ return KERN_SUCCESS;
+ }
+
+ cpu_affinity_zero(&newmask);
+
+ s = splsched();
+ thread_lock(thread);
+
+ for (i = 0; i < countp; i++) {
+ p = convert_port_to_processor_name(
+ (ipc_port_t)processor_list[i]);
+ if (p == PROCESSOR_NULL)
+ continue;
+
+ /* check pset membership */
+ if (p->processor_set != thread->processor_set) {
+ thread_unlock(thread);
+ splx(s);
+ return KERN_INVALID_ARGUMENT;
+ }
+
+ cpu_affinity_set(&newmask, (unsigned)p->slot_num);
+
+ /* require at least one processor online */
+ if (p->state == PROCESSOR_OFF_LINE)
+ continue;
+ any = TRUE;
+ }
+
+ if (!any) {
+ thread_unlock(thread);
+ splx(s);
+ return KERN_INVALID_ARGUMENT; /* empty effective set */
+ }
+
+ thread->affinity = newmask;
+ thread->affinity_set = TRUE;
+
+ /*
+ * Kick thread to its own processor
+ */
+#if NCPUS > 1
+ for (i = 0; i < smp_get_numcpus(); i++) {
+ p = cpu_to_processor(i);
+
+ if (p == PROCESSOR_NULL || p->state == PROCESSOR_OFF_LINE)
+ continue;
+
+ if (percpu_array[i].active_thread == thread) {
+ if (!cpu_affinity_test(&thread->affinity, i))
+ cause_ast_check(p);
+ break;
+ }
+ }
+#endif
+
+ thread_unlock(thread);
+ splx(s);
+
+ return KERN_SUCCESS;
+}
+
+/*
+ * thread_get_affinity
+ *
+ * Return the thread affinity set. If thread doesn't have any affinity set,
+ * returns a void processor_list.
+ *
+ */
+kern_return_t
+thread_get_affinity(
+ thread_t thread,
+ processor_name_array_t *processor_list,
+ natural_t *countp)
+{
+ int i, n = 0;
+ natural_t count;
+ ipc_port_t *tp;
+ spl_t s;
+
+ if (thread == THREAD_NULL)
+ return KERN_INVALID_ARGUMENT;
+
+ s = splsched();
+ thread_lock(thread);
+
+ if (thread->affinity_set == FALSE) {
+ thread_unlock(thread);
+ splx(s);
+ *processor_list = NULL;
+ *countp = 0;
+
+ return KERN_SUCCESS;
+ }
+
+ count = cpu_affinity_count(&thread->affinity);
+ assert(count <= NCPUS);
+
+ tp = (ipc_port_t *)kalloc(count * sizeof(ipc_port_t));
+ if (tp == NULL) {
+ thread_unlock(thread);
+ splx(s);
+
+ return KERN_RESOURCE_SHORTAGE;
+ }
+
+ for (i = 0; i < NCPUS; i++) {
+ if (cpu_affinity_test(&thread->affinity, i))
+ tp[n++] = convert_processor_name_to_port(
+ cpu_to_processor(i));
+ }
+
+ thread_unlock(thread);
+ splx(s);
+
+ *countp = count;
+ *processor_list = (mach_port_t *)tp;
+
+ return KERN_SUCCESS;
+}
diff --git a/kern/thread.h b/kern/thread.h
index e5128dad..b525c7f6 100644
--- a/kern/thread.h
+++ b/kern/thread.h
@@ -34,6 +34,7 @@
#ifndef _KERN_THREAD_H_
#define _KERN_THREAD_H_
+#include <string.h>
#include <mach/boolean.h>
#include <mach/thread_info.h>
#include <mach/thread_status.h>
@@ -54,6 +55,48 @@
#include <machine/thread.h>
#include <ipc/ipc_kmsg_queue.h>
+typedef struct cpu_affinity {
+ uint32_t bits[(NCPUS + 31) / 32];
+} cpu_affinity_t;
+
+static inline void
+cpu_affinity_zero(cpu_affinity_t *m) {
+ memset(m->bits, 0, sizeof(m->bits));
+}
+
+static inline void
+cpu_affinity_set(cpu_affinity_t *m, unsigned cpu) {
+ m->bits[cpu >> 5] |= (1u << (cpu & 31));
+}
+
+static inline boolean_t
+cpu_affinity_test(const cpu_affinity_t *m, unsigned cpu) {
+ return (m->bits[cpu >> 5] & (1u << (cpu & 31))) != 0;
+}
+
+static inline unsigned int
+popcnt_uint32(uint32_t n) {
+ unsigned int count = 0;
+
+ while (n) {
+ n &= n - 1;
+ count++;
+ }
+
+ return count;
+}
+
+static inline natural_t
+cpu_affinity_count(const cpu_affinity_t *m) {
+ natural_t total = 0;
+ int i;
+
+ for (i = 0; i < sizeof(m->bits) / sizeof(m->bits[0]); i++)
+ total += popcnt_uint32(m->bits[i]);
+
+ return total;
+}
+
/*
* Thread name buffer size. Use the same size as the task so
* the thread can inherit the task's name.
@@ -223,6 +266,8 @@ struct thread {
/* Processor data structures */
processor_set_t processor_set; /* assigned processor set */
processor_t bound_processor; /* bound to processor ?*/
+ cpu_affinity_t affinity; /* cpu affinity mask */
+ boolean_t affinity_set; /* if FALSE, use default mask */
sample_control_t pc_sample;
--
2.53.0