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


Reply via email to