diff options
author | Ian Moffett <ian@osmora.org> | 2025-07-17 17:44:05 -0400 |
---|---|---|
committer | Ian Moffett <ian@osmora.org> | 2025-07-17 18:35:22 -0400 |
commit | 3c9e0969840d47a812f5e65fa43f694f2e7f3ff3 (patch) | |
tree | 107d4709904495e47436889ccb17418bdaefff93 | |
parent | 984b549864280696256cec135651770ff0b1251b (diff) |
kernel: sched: Add support for CPU pinning
This commit introduces support for pinning processes to specific cores
which can be useful for things like, keeping core-specific workthreads
running on their respective processors as well as mitigating very
specific cases of false-sharing and performance degradation caused by a
thread jumping between cores which requires cache-line entries to be thrashed
around between them. Threads pinned to a specific core will always see their
cached data in the same L1, L2, etc.
Signed-off-by: Ian Moffett <ian@osmora.org>
-rw-r--r-- | sys/include/arch/amd64/cpu.h | 1 | ||||
-rw-r--r-- | sys/include/sys/proc.h | 13 | ||||
-rw-r--r-- | sys/kern/kern_sched.c | 66 |
3 files changed, 80 insertions, 0 deletions
diff --git a/sys/include/arch/amd64/cpu.h b/sys/include/arch/amd64/cpu.h index a047cef..046b621 100644 --- a/sys/include/arch/amd64/cpu.h +++ b/sys/include/arch/amd64/cpu.h @@ -46,6 +46,7 @@ struct cpu_info { uint32_t apicid; uint32_t feat; + uint8_t id; /* MI Logical ID */ uint8_t model : 4; /* CPU model number */ uint8_t family : 4; /* CPU family ID */ uint8_t has_x2apic : 1; diff --git a/sys/include/sys/proc.h b/sys/include/sys/proc.h index 972d3c4..9cc9238 100644 --- a/sys/include/sys/proc.h +++ b/sys/include/sys/proc.h @@ -74,6 +74,14 @@ struct __packed coredump { uint32_t checksum; }; +/* + * Sometimes we may need to pin a process + * to a specific CPU. This type represents + * the (machine independent) logical processor + * ID for a process to be pinned to. + */ +typedef int16_t affinity_t; + struct proc { pid_t pid; struct exec_prog exec; @@ -86,6 +94,7 @@ struct proc { struct trapframe tf; struct pcb pcb; struct proc *parent; + affinity_t affinity; void *data; size_t priority; int exit_status; @@ -107,10 +116,14 @@ struct proc { #define PROC_WAITED BIT(4) /* Being waited on by parent */ #define PROC_KTD BIT(5) /* Kernel thread */ #define PROC_SLEEP BIT(6) /* Thread execution paused */ +#define PROC_PINNED BIT(7) /* Pinned to CPU */ struct proc *this_td(void); struct proc *get_child(struct proc *cur, pid_t pid); +void proc_pin(struct proc *td, affinity_t cpu); +void proc_unpin(struct proc *td); + void proc_reap(struct proc *td); void proc_coredump(struct proc *td, uintptr_t fault_addr); diff --git a/sys/kern/kern_sched.c b/sys/kern/kern_sched.c index e259a2c..23a1ebb 100644 --- a/sys/kern/kern_sched.c +++ b/sys/kern/kern_sched.c @@ -78,13 +78,37 @@ sched_oneshot(bool now) timer.oneshot_us(usec); } +/* + * Returns true if a processor is associated + * with a specific thread + * + * @ci: CPU that wants to take 'td' + * @td: Thread to check against + */ +static bool +cpu_is_assoc(struct cpu_info *ci, struct proc *td) +{ + /* + * If we are not pinned, any processor is + * associated. + */ + if (!ISSET(td->flags, PROC_PINNED)) { + return true; + } + + return ci->id == td->affinity; +} + struct proc * sched_dequeue_td(void) { struct sched_queue *queue; struct proc *td = NULL; + struct cpu_info *ci; + uint32_t ncpu = 0; spinlock_acquire(&tdq_lock); + ci = this_cpu(); for (size_t i = 0; i < SCHED_NQUEUE; ++i) { queue = &qlist[i]; @@ -104,6 +128,19 @@ sched_dequeue_td(void) } } + /* + * If we are on a multicore system and this isn't + * our process, don't take it. Some threads might + * be pinned to a specific processor. + */ + ncpu = cpu_count(); + while (!cpu_is_assoc(ci, td) && ncpu > 1) { + td = TAILQ_NEXT(td, link); + if (td == NULL) { + break; + } + } + if (td == NULL) { continue; } @@ -249,6 +286,35 @@ sched_detach(struct proc *td) spinlock_release(&tdq_lock); } +/* + * Pin a process to a specific processor + * + * @td: Process to pin + * @cpu: Logical processor ID to pin `td' to. + * + * XXX: 'cpu' is a machine independent value, representing + * CPU<n> + */ +void +proc_pin(struct proc *td, affinity_t cpu) +{ + td->affinity = cpu; + td->flags |= PROC_PINNED; +} + +/* + * Unpin a pinned process, allowing it to be + * picked up by any processor + * + * @td: Process to unpin + */ +void +proc_unpin(struct proc *td) +{ + td->affinity = 0; + td->flags &= ~PROC_PINNED; +} + void sched_init(void) { |