From 9c113163fb2bb182d320f0228312c13601341163 Mon Sep 17 00:00:00 2001 From: jhb Date: Fri, 14 Mar 2008 19:41:48 +0000 Subject: Add preliminary support for binding interrupts to CPUs: - Add a new intr_event method ie_assign_cpu() that is invoked when the MI code wishes to bind an interrupt source to an individual CPU. The MD code may reject the binding with an error. If an assign_cpu function is not provided, then the kernel assumes the platform does not support binding interrupts to CPUs and fails all requests to do so. - Bind ithreads to CPUs on their next execution loop once an interrupt event is bound to a CPU. Only shared ithreads are bound. We currently leave private ithreads for drivers using filters + ithreads in the INTR_FILTER case unbound. - A new intr_event_bind() routine is used to bind an interrupt event to a CPU. - Implement binding on amd64 and i386 by way of the existing pic_assign_cpu PIC method. - For x86, provide a 'intr_bind(IRQ, cpu)' wrapper routine that looks up an interrupt source and binds its interrupt event to the specified CPU. MI code can currently (ab)use this by doing: intr_bind(rman_get_start(irq_res), cpu); however, I plan to add a truly MI interface (probably a bus_bind_intr(9)) where the implementation in the x86 nexus(4) driver would end up calling intr_bind() internally. Requested by: kmacy, gallatin, jeff Tested on: {amd64, i386} x {regular, INTR_FILTER} --- sys/kern/kern_intr.c | 96 +++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 92 insertions(+), 4 deletions(-) (limited to 'sys/kern/kern_intr.c') diff --git a/sys/kern/kern_intr.c b/sys/kern/kern_intr.c index d9b983a..3d02073 100644 --- a/sys/kern/kern_intr.c +++ b/sys/kern/kern_intr.c @@ -46,6 +46,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include @@ -240,7 +241,8 @@ intr_event_update(struct intr_event *ie) #ifndef INTR_FILTER int intr_event_create(struct intr_event **event, void *source, int flags, - void (*enable)(void *), const char *fmt, ...) + void (*enable)(void *), int (*assign_cpu)(void *, u_char), const char *fmt, + ...) { struct intr_event *ie; va_list ap; @@ -251,7 +253,9 @@ intr_event_create(struct intr_event **event, void *source, int flags, ie = malloc(sizeof(struct intr_event), M_ITHREAD, M_WAITOK | M_ZERO); ie->ie_source = source; ie->ie_enable = enable; + ie->ie_assign_cpu = assign_cpu; ie->ie_flags = flags; + ie->ie_cpu = NOCPU; TAILQ_INIT(&ie->ie_handlers); mtx_init(&ie->ie_lock, "intr event", NULL, MTX_DEF); @@ -271,7 +275,7 @@ intr_event_create(struct intr_event **event, void *source, int flags, int intr_event_create(struct intr_event **event, void *source, int flags, void (*enable)(void *), void (*eoi)(void *), void (*disab)(void *), - const char *fmt, ...) + int (*assign_cpu)(void *, u_char), const char *fmt, ...) { struct intr_event *ie; va_list ap; @@ -282,9 +286,11 @@ intr_event_create(struct intr_event **event, void *source, int flags, ie = malloc(sizeof(struct intr_event), M_ITHREAD, M_WAITOK | M_ZERO); ie->ie_source = source; ie->ie_enable = enable; + ie->ie_assign_cpu = assign_cpu; ie->ie_eoi = eoi; ie->ie_disab = disab; ie->ie_flags = flags; + ie->ie_cpu = NOCPU; TAILQ_INIT(&ie->ie_handlers); mtx_init(&ie->ie_lock, "intr event", NULL, MTX_DEF); @@ -302,6 +308,52 @@ intr_event_create(struct intr_event **event, void *source, int flags, } #endif +/* + * Bind an interrupt event to the specified CPU. Note that not all + * platforms support binding an interrupt to a CPU. For those + * platforms this request will fail. For supported platforms, any + * associated ithreads as well as the primary interrupt context will + * be bound to the specificed CPU. Using a cpu id of NOCPU unbinds + * the interrupt event. + */ +int +intr_event_bind(struct intr_event *ie, u_char cpu) +{ + struct thread *td; + int error; + + /* Need a CPU to bind to. */ + if (cpu != NOCPU && CPU_ABSENT(cpu)) + return (EINVAL); + + if (ie->ie_assign_cpu == NULL) + return (EOPNOTSUPP); + + /* Don't allow a bind request if the interrupt is already bound. */ + mtx_lock(&ie->ie_lock); + if (ie->ie_cpu != NOCPU && cpu != NOCPU) { + mtx_unlock(&ie->ie_lock); + return (EBUSY); + } + mtx_unlock(&ie->ie_lock); + + error = ie->ie_assign_cpu(ie->ie_source, cpu); + if (error) + return (error); + mtx_lock(&ie->ie_lock); + if (ie->ie_thread != NULL) + td = ie->ie_thread->it_thread; + else + td = NULL; + if (td != NULL) + thread_lock(td); + ie->ie_cpu = cpu; + if (td != NULL) + thread_unlock(td); + mtx_unlock(&ie->ie_lock); + return (0); +} + int intr_event_destroy(struct intr_event *ie) { @@ -893,10 +945,10 @@ swi_add(struct intr_event **eventp, const char *name, driver_intr_t handler, } else { #ifdef INTR_FILTER error = intr_event_create(&ie, NULL, IE_SOFT, - NULL, NULL, NULL, "swi%d:", pri); + NULL, NULL, NULL, NULL, "swi%d:", pri); #else error = intr_event_create(&ie, NULL, IE_SOFT, - NULL, "swi%d:", pri); + NULL, NULL, "swi%d:", pri); #endif if (error) return (error); @@ -1078,6 +1130,7 @@ ithread_loop(void *arg) struct intr_event *ie; struct thread *td; struct proc *p; + u_char cpu; td = curthread; p = td->td_proc; @@ -1086,6 +1139,7 @@ ithread_loop(void *arg) ("%s: ithread and proc linkage out of sync", __func__)); ie = ithd->it_event; ie->ie_count = 0; + cpu = NOCPU; /* * As long as we have interrupts outstanding, go through the @@ -1131,6 +1185,21 @@ ithread_loop(void *arg) ie->ie_count = 0; mi_switch(SW_VOL, NULL); } + +#ifdef SMP + /* + * Ensure we are bound to the correct CPU. We can't + * move ithreads until SMP is running however, so just + * leave interrupts on the boor CPU during boot. + */ + if (ie->ie_cpu != cpu && smp_started) { + cpu = ie->ie_cpu; + if (cpu == NOCPU) + sched_unbind(td); + else + sched_bind(td, cpu); + } +#endif thread_unlock(td); } } @@ -1147,6 +1216,7 @@ ithread_loop(void *arg) struct thread *td; struct proc *p; int priv; + u_char cpu; td = curthread; p = td->td_proc; @@ -1157,6 +1227,7 @@ ithread_loop(void *arg) ("%s: ithread and proc linkage out of sync", __func__)); ie = ithd->it_event; ie->ie_count = 0; + cpu = NOCPU; /* * As long as we have interrupts outstanding, go through the @@ -1205,6 +1276,21 @@ ithread_loop(void *arg) ie->ie_count = 0; mi_switch(SW_VOL, NULL); } + +#ifdef SMP + /* + * Ensure we are bound to the correct CPU. We can't + * move ithreads until SMP is running however, so just + * leave interrupts on the boor CPU during boot. + */ + if (!priv && ie->ie_cpu != cpu && smp_started) { + cpu = ie->ie_cpu; + if (cpu == NOCPU) + sched_unbind(td); + else + sched_bind(td, cpu); + } +#endif thread_unlock(td); } } @@ -1440,6 +1526,8 @@ db_dump_intr_event(struct intr_event *ie, int handlers) db_printf("(pid %d)", it->it_thread->td_proc->p_pid); else db_printf("(no thread)"); + if (ie->ie_cpu != NOCPU) + db_printf(" (CPU %d)", ie->ie_cpu); if ((ie->ie_flags & (IE_SOFT | IE_ENTROPY | IE_ADDING_THREAD)) != 0 || (it != NULL && it->it_need)) { db_printf(" {"); -- cgit v1.1