Module Name: src Committed By: knakahara Date: Mon Apr 27 06:42:53 UTC 2015
Modified Files: src/distrib/sets/lists/comp: mi src/share/man/man9: Makefile src/sys/arch/x86/include: intr.h pci_machdep.h pci_machdep_common.h src/sys/arch/x86/pci: pci_intr_machdep.c src/sys/arch/x86/x86: intr.c src/sys/kern: kern_stub.c src/sys/sys: intr.h Added Files: src/share/man/man9: pci_intr_distribute.9 src/sys/arch/x86/include: intr_distribute.h Log Message: add pci_intr_distribute(9) for x86. To generate a diff of this commit: cvs rdiff -u -r1.1954 -r1.1955 src/distrib/sets/lists/comp/mi cvs rdiff -u -r1.384 -r1.385 src/share/man/man9/Makefile cvs rdiff -u -r0 -r1.1 src/share/man/man9/pci_intr_distribute.9 cvs rdiff -u -r1.45 -r1.46 src/sys/arch/x86/include/intr.h cvs rdiff -u -r0 -r1.1 src/sys/arch/x86/include/intr_distribute.h cvs rdiff -u -r1.15 -r1.16 src/sys/arch/x86/include/pci_machdep.h cvs rdiff -u -r1.13 -r1.14 src/sys/arch/x86/include/pci_machdep_common.h cvs rdiff -u -r1.27 -r1.28 src/sys/arch/x86/pci/pci_intr_machdep.c cvs rdiff -u -r1.78 -r1.79 src/sys/arch/x86/x86/intr.c cvs rdiff -u -r1.38 -r1.39 src/sys/kern/kern_stub.c cvs rdiff -u -r1.17 -r1.18 src/sys/sys/intr.h Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/distrib/sets/lists/comp/mi diff -u src/distrib/sets/lists/comp/mi:1.1954 src/distrib/sets/lists/comp/mi:1.1955 --- src/distrib/sets/lists/comp/mi:1.1954 Tue Apr 21 11:10:29 2015 +++ src/distrib/sets/lists/comp/mi Mon Apr 27 06:42:52 2015 @@ -1,4 +1,4 @@ -# $NetBSD: mi,v 1.1954 2015/04/21 11:10:29 pooka Exp $ +# $NetBSD: mi,v 1.1955 2015/04/27 06:42:52 knakahara Exp $ # # Note: don't delete entries from here - mark them as "obsolete" instead. # @@ -17131,6 +17131,7 @@ ./usr/share/man/html9/in_getifa.html comp-sys-htmlman html ./usr/share/man/html9/incore.html comp-sys-htmlman html ./usr/share/man/html9/inittodr.html comp-sys-htmlman html +./usr/share/man/html9/pci_intr_distribute.html comp-sys-htmlman html ./usr/share/man/html9/intro.html comp-sys-htmlman html ./usr/share/man/html9/ioasic.html comp-sys-htmlman html ./usr/share/man/html9/ioasic_attach_devs.html comp-sys-htmlman html @@ -24063,6 +24064,7 @@ ./usr/share/man/man9/in_getifa.9 comp-sys-man .man ./usr/share/man/man9/incore.9 comp-sys-man .man ./usr/share/man/man9/inittodr.9 comp-sys-man .man +./usr/share/man/man9/pci_intr_distribute.9 comp-sys-man .man ./usr/share/man/man9/intro.9 comp-sys-man .man ./usr/share/man/man9/ioasic.9 comp-sys-man .man ./usr/share/man/man9/ioasic_attach_devs.9 comp-sys-man .man Index: src/share/man/man9/Makefile diff -u src/share/man/man9/Makefile:1.384 src/share/man/man9/Makefile:1.385 --- src/share/man/man9/Makefile:1.384 Tue Apr 21 11:10:29 2015 +++ src/share/man/man9/Makefile Mon Apr 27 06:42:52 2015 @@ -1,4 +1,4 @@ -# $NetBSD: Makefile,v 1.384 2015/04/21 11:10:29 pooka Exp $ +# $NetBSD: Makefile,v 1.385 2015/04/27 06:42:52 knakahara Exp $ # Makefile for section 9 (kernel function and variable) manual pages. @@ -39,10 +39,11 @@ MAN= accept_filter.9 accf_data.9 accf_ht microseq.9 microtime.9 microuptime.9 mi_switch.9 module.9 \ mstohz.9 mutex.9 m_tag.9 namecache.9 \ namei.9 nullop.9 opencrypto.9 optstr.9 \ - panic.9 pathbuf.9 pci.9 pci_configure_bus.9 pci_intr.9 pckbport.9 \ - pcmcia.9 pcq.9 pcu.9 percpu.9 pfil.9 physio.9 pmap.9 pmatch.9 \ - pmc.9 pmf.9 pool.9 pool_cache.9 powerhook_establish.9 ppi.9 \ - ppsratecheck.9 preempt.9 proc_find.9 pserialize.9 putter.9 \ + panic.9 pathbuf.9 pci.9 pci_configure_bus.9 pci_intr.9 \ + pci_intr_distribute.9 pckbport.9 pcmcia.9 pcq.9 pcu.9 \ + percpu.9 pfil.9 physio.9 pmap.9 pmatch.9 pmc.9 pmf.9 pool.9 \ + pool_cache.9 powerhook_establish.9 ppi.9 ppsratecheck.9 preempt.9 \ + proc_find.9 pserialize.9 putter.9 \ radio.9 ras.9 rasops.9 ratecheck.9 resettodr.9 rnd.9 rndsink.9 \ roundup.9 rssadapt.9 rt_timer.9 rwlock.9 RUN_ONCE.9 STACK.9 \ scanc.9 \ Index: src/sys/arch/x86/include/intr.h diff -u src/sys/arch/x86/include/intr.h:1.45 src/sys/arch/x86/include/intr.h:1.46 --- src/sys/arch/x86/include/intr.h:1.45 Sun Jul 20 15:46:34 2014 +++ src/sys/arch/x86/include/intr.h Mon Apr 27 06:42:52 2015 @@ -1,4 +1,4 @@ -/* $NetBSD: intr.h,v 1.45 2014/07/20 15:46:34 uebayasi Exp $ */ +/* $NetBSD: intr.h,v 1.46 2015/04/27 06:42:52 knakahara Exp $ */ /*- * Copyright (c) 1998, 2001, 2006, 2007, 2008 The NetBSD Foundation, Inc. @@ -42,6 +42,7 @@ #endif #include <sys/evcnt.h> +#include <sys/queue.h> #include <machine/intrdefs.h> #ifndef _LOCORE @@ -71,6 +72,11 @@ struct intrstub { void *ist_resume; }; +struct percpu_evcnt { + cpuid_t cpuid; + uint64_t count; +}; + struct intrsource { int is_maxlevel; /* max. IPL for this source */ int is_pin; /* IRQ for legacy; pin for IO APIC, @@ -86,6 +92,10 @@ struct intrsource { int is_idtvec; int is_minlevel; char is_evname[32]; /* event counter name */ + char is_intrid[INTRIDBUF]; /* intrid created by create_intrid() */ + cpuid_t is_active_cpu; /* active cpuid */ + struct percpu_evcnt *is_saved_evcnt; /* interrupt count of deactivated cpus */ + SIMPLEQ_ENTRY(intrsource) is_list; /* link of intrsources */ }; #define IS_LEGACY 0x0001 /* legacy ISA irq source */ @@ -182,6 +192,9 @@ int intr_find_mpmapping(int, int, int *) struct pic *intr_findpic(int); void intr_printconfig(void); +struct intrsource *intr_allocate_io_intrsource(const char *); +void intr_free_io_intrsource(const char *); + int x86_send_ipi(struct cpu_info *, int); void x86_broadcast_ipi(int); void x86_ipi_handler(void); Index: src/sys/arch/x86/include/pci_machdep.h diff -u src/sys/arch/x86/include/pci_machdep.h:1.15 src/sys/arch/x86/include/pci_machdep.h:1.16 --- src/sys/arch/x86/include/pci_machdep.h:1.15 Wed Mar 4 05:35:50 2015 +++ src/sys/arch/x86/include/pci_machdep.h Mon Apr 27 06:42:52 2015 @@ -1,4 +1,4 @@ -/* $NetBSD: pci_machdep.h,v 1.15 2015/03/04 05:35:50 knakahara Exp $ */ +/* $NetBSD: pci_machdep.h,v 1.16 2015/04/27 06:42:52 knakahara Exp $ */ /* * Copyright (c) 1996 Christopher G. Demetriou. All rights reserved. @@ -33,6 +33,8 @@ #ifndef _X86_PCI_MACHDEP_H_ #define _X86_PCI_MACHDEP_H_ +#include <x86/intr_distribute.h> + /* * Types provided to machine-independent PCI code * See also i82093var.h to find out pci_intr_handle_t's bitfield. Index: src/sys/arch/x86/include/pci_machdep_common.h diff -u src/sys/arch/x86/include/pci_machdep_common.h:1.13 src/sys/arch/x86/include/pci_machdep_common.h:1.14 --- src/sys/arch/x86/include/pci_machdep_common.h:1.13 Sat Mar 29 19:28:30 2014 +++ src/sys/arch/x86/include/pci_machdep_common.h Mon Apr 27 06:42:52 2015 @@ -1,4 +1,4 @@ -/* $NetBSD: pci_machdep_common.h,v 1.13 2014/03/29 19:28:30 christos Exp $ */ +/* $NetBSD: pci_machdep_common.h,v 1.14 2015/04/27 06:42:52 knakahara Exp $ */ /* * Copyright (c) 1996 Christopher G. Demetriou. All rights reserved. @@ -41,6 +41,8 @@ #define __HAVE_PCIIDE_MACHDEP_COMPAT_INTR_DISESTABLISH #endif +#include <sys/kcpuset.h> + /* * x86-specific PCI structure and type definitions. * NOT TO BE USED DIRECTLY BY MACHINE INDEPENDENT CODE. @@ -116,6 +118,7 @@ const struct evcnt *pci_intr_evcnt(pci_c void *pci_intr_establish(pci_chipset_tag_t, pci_intr_handle_t, int, int (*)(void *), void *); void pci_intr_disestablish(pci_chipset_tag_t, void *); +int pci_intr_distribute(void *, const kcpuset_t *, kcpuset_t *); /* experimental MSI support */ void *pci_msi_establish(struct pci_attach_args *, int, int (*)(void *), void *); Index: src/sys/arch/x86/pci/pci_intr_machdep.c diff -u src/sys/arch/x86/pci/pci_intr_machdep.c:1.27 src/sys/arch/x86/pci/pci_intr_machdep.c:1.28 --- src/sys/arch/x86/pci/pci_intr_machdep.c:1.27 Sat Mar 29 19:28:30 2014 +++ src/sys/arch/x86/pci/pci_intr_machdep.c Mon Apr 27 06:42:52 2015 @@ -1,4 +1,4 @@ -/* $NetBSD: pci_intr_machdep.c,v 1.27 2014/03/29 19:28:30 christos Exp $ */ +/* $NetBSD: pci_intr_machdep.c,v 1.28 2015/04/27 06:42:52 knakahara Exp $ */ /*- * Copyright (c) 1997, 1998, 2009 The NetBSD Foundation, Inc. @@ -73,15 +73,17 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: pci_intr_machdep.c,v 1.27 2014/03/29 19:28:30 christos Exp $"); +__KERNEL_RCSID(0, "$NetBSD: pci_intr_machdep.c,v 1.28 2015/04/27 06:42:52 knakahara Exp $"); #include <sys/types.h> #include <sys/param.h> #include <sys/time.h> #include <sys/systm.h> +#include <sys/cpu.h> #include <sys/errno.h> #include <sys/device.h> #include <sys/intr.h> +#include <sys/kmem.h> #include <sys/malloc.h> #include <dev/pci/pcivar.h> @@ -328,6 +330,15 @@ pci_intr_disestablish(pci_chipset_tag_t intr_disestablish(cookie); } +int +pci_intr_distribute(void *cookie, const kcpuset_t *newset, kcpuset_t *oldset) +{ + + /* XXX Is pc_ov->ov_intr_distribute required? */ + + return intr_distribute(cookie, newset, oldset); +} + #if NIOAPIC > 0 /* * experimental support for MSI, does support a single vector, Index: src/sys/arch/x86/x86/intr.c diff -u src/sys/arch/x86/x86/intr.c:1.78 src/sys/arch/x86/x86/intr.c:1.79 --- src/sys/arch/x86/x86/intr.c:1.78 Wed Apr 8 05:52:41 2015 +++ src/sys/arch/x86/x86/intr.c Mon Apr 27 06:42:52 2015 @@ -1,4 +1,4 @@ -/* $NetBSD: intr.c,v 1.78 2015/04/08 05:52:41 knakahara Exp $ */ +/* $NetBSD: intr.c,v 1.79 2015/04/27 06:42:52 knakahara Exp $ */ /*- * Copyright (c) 2007, 2008, 2009 The NetBSD Foundation, Inc. @@ -133,7 +133,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: intr.c,v 1.78 2015/04/08 05:52:41 knakahara Exp $"); +__KERNEL_RCSID(0, "$NetBSD: intr.c,v 1.79 2015/04/27 06:42:52 knakahara Exp $"); #include "opt_intrdebug.h" #include "opt_multiprocessor.h" @@ -180,6 +180,12 @@ __KERNEL_RCSID(0, "$NetBSD: intr.c,v 1.7 #include <ddb/db_output.h> #endif +#ifdef INTRDEBUG +#define DPRINTF(msg) printf msg +#else +#define DPRINTF(msg) +#endif + struct pic softintr_pic = { .pic_name = "softintr_fakepic", .pic_type = PIC_SOFT, @@ -190,6 +196,9 @@ struct pic softintr_pic = { static void intr_calculatemasks(struct cpu_info *); +static SIMPLEQ_HEAD(, intrsource) io_interrupt_sources = + SIMPLEQ_HEAD_INITIALIZER(io_interrupt_sources); + #if NIOAPIC > 0 || NACPICA > 0 static int intr_scan_bus(int, int, int *); #if NPCI > 0 @@ -197,9 +206,11 @@ static int intr_find_pcibridge(int, pcit #endif #endif -static int intr_allocate_slot_cpu(struct cpu_info *, struct pic *, int, int *); +static int intr_allocate_slot_cpu(struct cpu_info *, struct pic *, int, int *, + struct intrsource *); static int __noinline intr_allocate_slot(struct pic *, int, int, - struct cpu_info **, int *, int *); + struct cpu_info **, int *, int *, + struct intrsource *); static void intr_source_free(struct cpu_info *, int, struct pic *, int); @@ -214,6 +225,14 @@ static void intr_redistribute_xc_s1(void static void intr_redistribute_xc_s2(void *, void *); static bool intr_redistribute(struct cpu_info *); +static const char *legacy_intr_string(int, char *, size_t, struct pic *); + +static int intr_find_unused_slot(struct cpu_info *, int *); +static void intr_activate_xcall(void *, void *); +static void intr_deactivate_xcall(void *, void *); +static void intr_get_affinity(struct intrsource *, kcpuset_t *); +static int intr_set_affinity(struct intrsource *, const kcpuset_t *); + /* * Fill in default interrupt table (in case of spurious interrupt * during configuration of kernel), setup interrupt control unit @@ -432,9 +451,135 @@ intr_scan_bus(int bus, int pin, int *han } #endif +/* + * Create an interrupt id such as "ioapic0 pin 9". This interrupt id is used + * by MI code and intrctl(8). + */ +static const char * +create_intrid(int legacy_irq, struct pic *pic, int pin, char *buf, size_t len) +{ + int ih; + + /* + * If the device is pci, "legacy_irq" is alway -1. Least 8 bit of "ih" + * is only used in intr_string() to show the irq number. + * If the device is "legacy"(such as floppy), it should not use + * intr_string(). + */ + if (pic->pic_type == PIC_I8259) { + ih = legacy_irq; + return legacy_intr_string(ih, buf, len, pic); + } else { + ih = ((pic->pic_apicid << APIC_INT_APIC_SHIFT) & APIC_INT_APIC_MASK) | + ((pin << APIC_INT_PIN_SHIFT) & APIC_INT_PIN_MASK); + if (pic->pic_type == PIC_IOAPIC) { + ih |= APIC_INT_VIA_APIC; + } + ih |= pin; + return intr_string(ih, buf, len); + } +} + +/* + * Find intrsource from io_interrupt_sources list. + */ +static struct intrsource * +intr_get_io_intrsource(const char *intrid) +{ + struct intrsource *isp; + + KASSERT(mutex_owned(&cpu_lock)); + + SIMPLEQ_FOREACH(isp, &io_interrupt_sources, is_list) { + KASSERT(isp->is_intrid != NULL); + if (strncmp(intrid, isp->is_intrid, INTRIDBUF - 1) == 0) { + return isp; + } + } + return NULL; +} + +/* + * Allocate intrsource and add to io_interrupt_sources list. + */ +struct intrsource * +intr_allocate_io_intrsource(const char *intrid) +{ + CPU_INFO_ITERATOR cii; + struct cpu_info *ci; + struct intrsource *isp; + struct percpu_evcnt *pep; + + KASSERT(mutex_owned(&cpu_lock)); + + if (intrid == NULL) + return NULL; + + isp = kmem_zalloc(sizeof(*isp), KM_SLEEP); + if (isp == NULL) { + return NULL; + } + + pep = kmem_zalloc(sizeof(*pep) * ncpu, KM_SLEEP); + if (pep == NULL) { + kmem_free(isp, sizeof(*isp)); + return NULL; + } + isp->is_saved_evcnt = pep; + for (CPU_INFO_FOREACH(cii, ci)) { + pep->cpuid = ci->ci_cpuid; + pep++; + } + strncpy(isp->is_intrid, intrid, sizeof(isp->is_intrid)); + + SIMPLEQ_INSERT_TAIL(&io_interrupt_sources, isp, is_list); + + return isp; +} + +/* + * Remove from io_interrupt_sources list and free by the intrsource pointer. + */ +static void +intr_free_io_intrsource_direct(struct intrsource *isp) +{ + KASSERT(mutex_owned(&cpu_lock)); + + SIMPLEQ_REMOVE(&io_interrupt_sources, isp, intrsource, is_list); + + /* Is this interrupt established? */ + if (isp->is_evname != '\0') + evcnt_detach(&isp->is_evcnt); + + kmem_free(isp->is_saved_evcnt, + sizeof(*(isp->is_saved_evcnt)) * ncpu); + kmem_free(isp, sizeof(*isp)); +} + +/* + * Remove from io_interrupt_sources list and free by the interrupt id. + * This function can be used by MI code. + */ +void +intr_free_io_intrsource(const char *intrid) +{ + struct intrsource *isp; + + KASSERT(mutex_owned(&cpu_lock)); + + if (intrid == NULL) + return; + + if ((isp = intr_get_io_intrsource(intrid)) == NULL) { + return; + } + + intr_free_io_intrsource_direct(isp); +} + static int intr_allocate_slot_cpu(struct cpu_info *ci, struct pic *pic, int pin, - int *index) + int *index, struct intrsource *chained) { int slot, i; struct intrsource *isp; @@ -464,14 +609,13 @@ intr_allocate_slot_cpu(struct cpu_info * isp = ci->ci_isources[slot]; if (isp == NULL) { - isp = kmem_zalloc(sizeof(*isp), KM_SLEEP); - if (isp == NULL) { - return ENOMEM; - } + isp = chained; + KASSERT(isp != NULL); snprintf(isp->is_evname, sizeof (isp->is_evname), "pin %d", pin); evcnt_attach_dynamic(&isp->is_evcnt, EVCNT_TYPE_INTR, NULL, pic->pic_name, isp->is_evname); + isp->is_active_cpu = ci->ci_cpuid; ci->ci_isources[slot] = isp; } @@ -484,7 +628,8 @@ intr_allocate_slot_cpu(struct cpu_info * */ static int __noinline intr_allocate_slot(struct pic *pic, int pin, int level, - struct cpu_info **cip, int *index, int *idt_slot) + struct cpu_info **cip, int *index, int *idt_slot, + struct intrsource *chained) { CPU_INFO_ITERATOR cii; struct cpu_info *ci, *lci; @@ -523,7 +668,7 @@ intr_allocate_slot(struct pic *pic, int * Must be directed to BP. */ ci = &cpu_info_primary; - error = intr_allocate_slot_cpu(ci, pic, pin, &slot); + error = intr_allocate_slot_cpu(ci, pic, pin, &slot, chained); } else { /* * Find least loaded AP/BP and try to allocate there. @@ -543,7 +688,7 @@ intr_allocate_slot(struct pic *pic, int #endif } KASSERT(ci != NULL); - error = intr_allocate_slot_cpu(ci, pic, pin, &slot); + error = intr_allocate_slot_cpu(ci, pic, pin, &slot, chained); /* * If that did not work, allocate anywhere. @@ -555,7 +700,7 @@ intr_allocate_slot(struct pic *pic, int continue; } error = intr_allocate_slot_cpu(ci, pic, - pin, &slot); + pin, &slot, chained); if (error == 0) { break; } @@ -578,7 +723,6 @@ intr_allocate_slot(struct pic *pic, int } if (idtvec == 0) { evcnt_detach(&ci->ci_isources[slot]->is_evcnt); - kmem_free(ci->ci_isources[slot], sizeof(*(ci->ci_isources[slot]))); ci->ci_isources[slot] = NULL; return EBUSY; } @@ -599,9 +743,6 @@ intr_source_free(struct cpu_info *ci, in if (isp->is_handlers != NULL) return; ci->ci_isources[slot] = NULL; - evcnt_detach(&isp->is_evcnt); - kmem_free(isp, sizeof(*isp)); - ci->ci_isources[slot] = NULL; if (pic != &i8259_pic) idt_vec_free(idtvec); } @@ -705,11 +846,13 @@ intr_establish(int legacy_irq, struct pi struct intrhand **p, *q, *ih; struct cpu_info *ci; int slot, error, idt_vec; - struct intrsource *source; + struct intrsource *chained, *source; #ifdef MULTIPROCESSOR bool mpsafe = (known_mpsafe || level != IPL_VM); #endif /* MULTIPROCESSOR */ uint64_t where; + const char *intrstr; + char intrstr_buf[INTRIDBUF]; #ifdef DIAGNOSTIC if (legacy_irq != -1 && (legacy_irq < 0 || legacy_irq > 15)) @@ -725,9 +868,27 @@ intr_establish(int legacy_irq, struct pi return NULL; } + intrstr = create_intrid(legacy_irq, pic, pin, intrstr_buf, + sizeof(intrstr_buf)); + KASSERT(intrstr != NULL); + mutex_enter(&cpu_lock); - error = intr_allocate_slot(pic, pin, level, &ci, &slot, &idt_vec); + + /* allocate intrsource pool, if not yet. */ + chained = intr_get_io_intrsource(intrstr); + if (chained == NULL) { + chained = intr_allocate_io_intrsource(intrstr); + if (chained == NULL) { + mutex_exit(&cpu_lock); + printf("%s: can't allocate io_intersource\n", __func__); + return NULL; + } + } + + error = intr_allocate_slot(pic, pin, level, &ci, &slot, &idt_vec, + chained); if (error != 0) { + intr_free_io_intrsource_direct(chained); mutex_exit(&cpu_lock); kmem_free(ih, sizeof(*ih)); printf("failed to allocate interrupt slot for PIC %s pin %d\n", @@ -739,6 +900,7 @@ intr_establish(int legacy_irq, struct pi if (source->is_handlers != NULL && source->is_pic->pic_type != pic->pic_type) { + intr_free_io_intrsource_direct(chained); mutex_exit(&cpu_lock); kmem_free(ih, sizeof(*ih)); printf("%s: can't share intr source between " @@ -761,9 +923,10 @@ intr_establish(int legacy_irq, struct pi /* FALLTHROUGH */ case IST_PULSE: if (type != IST_NONE) { + intr_source_free(ci, slot, pic, idt_vec); + intr_free_io_intrsource_direct(chained); mutex_exit(&cpu_lock); kmem_free(ih, sizeof(*ih)); - intr_source_free(ci, slot, pic, idt_vec); printf("%s: pic %s pin %d: can't share " "type %d with %d\n", __func__, pic->pic_name, pin, @@ -902,6 +1065,19 @@ intr_disestablish_xcall(void *arg1, void #endif } +static int +intr_num_handlers(struct intrsource *isp) +{ + struct intrhand *ih; + int num; + + num = 0; + for (ih = isp->is_handlers; ih != NULL; ih = ih->ih_next) + num++; + + return num; +} + /* * Deregister an interrupt handler. */ @@ -909,6 +1085,7 @@ void intr_disestablish(struct intrhand *ih) { struct cpu_info *ci; + struct intrsource *isp; uint64_t where; /* @@ -920,16 +1097,39 @@ intr_disestablish(struct intrhand *ih) ci = ih->ih_cpu; (ci->ci_nintrhand)--; KASSERT(ci->ci_nintrhand >= 0); + isp = ci->ci_isources[ih->ih_slot]; if (ci == curcpu() || !mp_online) { intr_disestablish_xcall(ih, NULL); } else { where = xc_unicast(0, intr_disestablish_xcall, ih, NULL, ci); xc_wait(where); } + + if (intr_num_handlers(isp) < 1) { + intr_free_io_intrsource_direct(isp); + } + mutex_exit(&cpu_lock); + kmem_free(ih, sizeof(*ih)); } +static const char * +legacy_intr_string(int ih, char *buf, size_t len, struct pic *pic) +{ + int legacy_irq; + + KASSERT(pic->pic_type == PIC_I8259); + KASSERT(APIC_IRQ_ISLEGACY(ih)); + + legacy_irq = APIC_IRQ_LEGACY_IRQ(ih); + KASSERT(legacy_irq >= 0 && legacy_irq < 16); + + snprintf(buf, len, "%s pin %d", pic->pic_name, legacy_irq); + + return buf; +} + const char * intr_string(int ih, char *buf, size_t len) { @@ -940,7 +1140,6 @@ intr_string(int ih, char *buf, size_t le if (ih == 0) panic("%s: bogus handle 0x%x", __func__, ih); - #if NIOAPIC > 0 if (ih & APIC_INT_VIA_APIC) { pic = ioapic_find(APIC_IRQ_APIC(ih)); @@ -1179,6 +1378,46 @@ softint_init_md(lwp_t *l, u_int level, u intr_calculatemasks(ci); } +/* + * Save current affinitied cpu's interrupt count. + */ +static void +intr_save_evcnt(struct intrsource *source, cpuid_t cpuid) +{ + struct percpu_evcnt *pep; + uint64_t curcnt; + int i; + + curcnt = source->is_evcnt.ev_count; + pep = source->is_saved_evcnt; + + for (i = 0; i < ncpu; i++) { + if (pep[i].cpuid == cpuid) { + pep[i].count = curcnt; + break; + } + } +} + +/* + * Restore current affinitied cpu's interrupt count. + */ +static void +intr_restore_evcnt(struct intrsource *source, cpuid_t cpuid) +{ + struct percpu_evcnt *pep; + int i; + + pep = source->is_saved_evcnt; + + for (i = 0; i < ncpu; i++) { + if (pep[i].cpuid == cpuid) { + source->is_evcnt.ev_count = pep[i].count; + break; + } + } +} + static void intr_redistribute_xc_t(void *arg1, void *arg2) { @@ -1352,6 +1591,9 @@ intr_redistribute(struct cpu_info *oci) nci->ci_nintrhand++; ih->ih_cpu = nci; } + intr_save_evcnt(isp, oci->ci_cpuid); + intr_restore_evcnt(isp, nci->ci_cpuid); + isp->is_active_cpu = nci->ci_cpuid; return true; } @@ -1386,3 +1628,263 @@ cpu_intr_count(struct cpu_info *ci) return ci->ci_nintrhand; } + +static int +intr_find_unused_slot(struct cpu_info *ci, int *index) +{ + int slot, i; + + KASSERT(mutex_owned(&cpu_lock)); + + slot = -1; + for (i = 0; i < MAX_INTR_SOURCES ; i++) { + if (ci->ci_isources[i] == NULL) { + slot = i; + break; + } + } + if (slot == -1) { + DPRINTF(("cannot allocate ci_isources\n")); + return EBUSY; + } + + *index = slot; + return 0; +} + +/* + * Let cpu_info ready to accept the interrupt. + */ +static void +intr_activate_xcall(void *arg1, void *arg2) +{ + struct cpu_info *ci; + struct intrsource *source; + struct intrstub *stubp; + struct intrhand *ih; + u_long psl; + int idt_vec; + int slot; + + ih = arg1; + + kpreempt_disable(); + + KASSERT(ih->ih_cpu == curcpu() || !mp_online); + + ci = ih->ih_cpu; + slot = ih->ih_slot; + source = ci->ci_isources[slot]; + idt_vec = source->is_idtvec; + + psl = x86_read_psl(); + x86_disable_intr(); + + intr_calculatemasks(ci); + + if (source->is_type == IST_LEVEL) { + stubp = &source->is_pic->pic_level_stubs[slot]; + } else { + stubp = &source->is_pic->pic_edge_stubs[slot]; + } + source->is_resume = stubp->ist_resume; + source->is_recurse = stubp->ist_recurse; + setgate(&idt[idt_vec], stubp->ist_entry, 0, SDT_SYS386IGT, + SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); + + x86_write_psl(psl); + + kpreempt_enable(); +} + +/* + * Let cpu_info not accept the interrupt. + */ +static void +intr_deactivate_xcall(void *arg1, void *arg2) +{ + struct cpu_info *ci; + struct intrhand *ih, *lih; + u_long psl; + int slot; + + ih = arg1; + + kpreempt_disable(); + + KASSERT(ih->ih_cpu == curcpu() || !mp_online); + + ci = ih->ih_cpu; + slot = ih->ih_slot; + + psl = x86_read_psl(); + x86_disable_intr(); + + /* Move all devices sharing IRQ number. */ + ci->ci_isources[slot] = NULL; + for (lih = ih; lih != NULL; lih = lih->ih_next) { + ci->ci_nintrhand--; + } + + intr_calculatemasks(ci); + + /* + * Skip unsetgate(), because the same itd[] entry is overwritten in + * intr_activate_xcall(). + */ + + x86_write_psl(psl); + + kpreempt_enable(); +} + +static void +intr_get_affinity(struct intrsource *isp, kcpuset_t *cpuset) +{ + struct cpu_info *ci; + + KASSERT(mutex_owned(&cpu_lock)); + + if (isp == NULL) { + kcpuset_zero(cpuset); + return; + } + + ci = isp->is_handlers->ih_cpu; + if (ci == NULL) { + kcpuset_zero(cpuset); + return; + } + + kcpuset_set(cpuset, cpu_index(ci)); + return; +} + +static int +intr_set_affinity(struct intrsource *isp, const kcpuset_t *cpuset) +{ + struct cpu_info *oldci, *newci; + struct intrhand *ih, *lih; + struct pic *pic; + u_int cpu_idx; + int idt_vec; + int oldslot, newslot; + int err; + int pin; + + KASSERT(mutex_owned(&cpu_lock)); + + /* XXX + * logical destination mode is not supported, use lowest index cpu. + */ + cpu_idx = kcpuset_ffs(cpuset) - 1; + newci = cpu_lookup(cpu_idx); + if (newci == NULL) { + DPRINTF(("invalid cpu index: %u\n", cpu_idx)); + return EINVAL; + } + if ((newci->ci_schedstate.spc_flags & SPCF_NOINTR) != 0) { + DPRINTF(("the cpu is set nointr shield. index:%u\n", cpu_idx)); + return EINVAL; + } + + if (isp == NULL) { + DPRINTF(("invalid intrctl handler\n")); + return EINVAL; + } + + /* i8259_pic supports only primary cpu, see i8259.c. */ + pic = isp->is_pic; + if (pic == &i8259_pic) { + DPRINTF(("i8259 pic does not support set_affinity\n")); + return ENOTSUP; + } + + ih = isp->is_handlers; + oldci = ih->ih_cpu; + if (newci == oldci) /* nothing to do */ + return 0; + + oldslot = ih->ih_slot; + idt_vec = isp->is_idtvec; + + err = intr_find_unused_slot(newci, &newslot); + if (err) { + DPRINTF(("failed to allocate interrupt slot for PIC %s intrid %s\n", + isp->is_pic->pic_name, isp->is_intrid)); + return err; + } + + pin = isp->is_pin; + (*pic->pic_hwmask)(pic, pin); /* for ci_ipending check */ + if (oldci->ci_ipending & (1 << oldslot)) { + (*pic->pic_hwunmask)(pic, pin); + DPRINTF(("pin %d on cpuid %ld has pending interrupts.\n", + pin, oldci->ci_cpuid)); + return EBUSY; + } + + kpreempt_disable(); + + /* deactivate old interrupt setting */ + if (oldci == curcpu() || !mp_online) { + intr_deactivate_xcall(ih, NULL); + } else { + uint64_t where; + where = xc_unicast(0, intr_deactivate_xcall, ih, + NULL, oldci); + xc_wait(where); + } + intr_save_evcnt(isp, oldci->ci_cpuid); + (*pic->pic_delroute)(pic, oldci, pin, idt_vec, isp->is_type); + + /* activate new interrupt setting */ + newci->ci_isources[newslot] = isp; + for (lih = ih; lih != NULL; lih = lih->ih_next) { + newci->ci_nintrhand++; + lih->ih_cpu = newci; + lih->ih_slot = newslot; + } + if (newci == curcpu() || !mp_online) { + intr_activate_xcall(ih, NULL); + } else { + uint64_t where; + where = xc_unicast(0, intr_activate_xcall, ih, + NULL, newci); + xc_wait(where); + } + intr_restore_evcnt(isp, newci->ci_cpuid); + isp->is_active_cpu = newci->ci_cpuid; + (*pic->pic_addroute)(pic, newci, pin, idt_vec, isp->is_type); + + kpreempt_enable(); + + (*pic->pic_hwunmask)(pic, pin); + + return err; +} + +int +intr_distribute(struct intrhand *ih, const kcpuset_t *newset, kcpuset_t *oldset) +{ + struct intrsource *isp; + int ret, slot; + + if (ih == NULL) + return EINVAL; + + mutex_enter(&cpu_lock); + + slot = ih->ih_slot; + isp = ih->ih_cpu->ci_isources[slot]; + KASSERT(isp != NULL); + + if (oldset != NULL) + intr_get_affinity(isp, oldset); + + ret = intr_set_affinity(isp, newset); + + mutex_exit(&cpu_lock); + + return ret; +} Index: src/sys/kern/kern_stub.c diff -u src/sys/kern/kern_stub.c:1.38 src/sys/kern/kern_stub.c:1.39 --- src/sys/kern/kern_stub.c:1.38 Mon Dec 9 18:06:27 2013 +++ src/sys/kern/kern_stub.c Mon Apr 27 06:42:53 2015 @@ -1,4 +1,4 @@ -/* $NetBSD: kern_stub.c,v 1.38 2013/12/09 18:06:27 pooka Exp $ */ +/* $NetBSD: kern_stub.c,v 1.39 2015/04/27 06:42:53 knakahara Exp $ */ /*- * Copyright (c) 2007, 2008 The NetBSD Foundation, Inc. @@ -62,7 +62,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: kern_stub.c,v 1.38 2013/12/09 18:06:27 pooka Exp $"); +__KERNEL_RCSID(0, "$NetBSD: kern_stub.c,v 1.39 2015/04/27 06:42:53 knakahara Exp $"); #include "opt_ptrace.h" #include "opt_ktrace.h" @@ -147,6 +147,8 @@ __weak_alias(userconf_prompt, voidop); __weak_alias(kobj_renamespace, nullop); +__weak_alias(pci_intr_distribute, eopnotsupp); + /* * Scheduler activations system calls. These need to remain until libc's * major version is bumped. Index: src/sys/sys/intr.h diff -u src/sys/sys/intr.h:1.17 src/sys/sys/intr.h:1.18 --- src/sys/sys/intr.h:1.17 Sun May 25 15:42:01 2014 +++ src/sys/sys/intr.h Mon Apr 27 06:42:53 2015 @@ -1,4 +1,4 @@ -/* $NetBSD: intr.h,v 1.17 2014/05/25 15:42:01 rmind Exp $ */ +/* $NetBSD: intr.h,v 1.18 2015/04/27 06:42:53 knakahara Exp $ */ /*- * Copyright (c) 2007 The NetBSD Foundation, Inc. @@ -32,6 +32,8 @@ #ifndef _SYS_INTR_H_ #define _SYS_INTR_H_ +#define INTRIDBUF 64 + #ifdef _KERNEL #include <sys/types.h> Added files: Index: src/share/man/man9/pci_intr_distribute.9 diff -u /dev/null src/share/man/man9/pci_intr_distribute.9:1.1 --- /dev/null Mon Apr 27 06:42:53 2015 +++ src/share/man/man9/pci_intr_distribute.9 Mon Apr 27 06:42:52 2015 @@ -0,0 +1,53 @@ +.\" $NetBSD: pci_intr_distribute.9,v 1.1 2015/04/27 06:42:52 knakahara Exp $ +.\" +.\" Copyright (c) 2015 Internet Initiative Japan Inc. +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS +.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +.\" TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS +.\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +.\" POSSIBILITY OF SUCH DAMAGE. +.\" +.Dd April 8, 2015 +.Dt PCI_INTR_DISTRIBUTE 9 +.Os +.Sh NAME +.Nm pci_intr_distribute +.Sh SYNOPSIS +.In dev/pci/pcivar.h +.Ft int +.Fn pci_intr_distribute "void *ich" "const kcpuset_t *newset" \ +"kcpuset_t *oldset" +.Sh DESCRIPTION +The +.Nm +functions exist to assing an interrupt to CPU. +.Pp +If a driver (or the other kernel component) wishes to assign an +interrupt to CPU, it should pass the return value of +.Fn pci_intr_establish +to the +.Ft ich . +And it should set kcpuset which want to assign to +.Ft newset . +If it want to get the assignment before changing, it should be +pass non-NULL value to +.Ft oldset . +If not, it should set NULL to +.Ft oldset . Index: src/sys/arch/x86/include/intr_distribute.h diff -u /dev/null src/sys/arch/x86/include/intr_distribute.h:1.1 --- /dev/null Mon Apr 27 06:42:53 2015 +++ src/sys/arch/x86/include/intr_distribute.h Mon Apr 27 06:42:52 2015 @@ -0,0 +1,40 @@ +/* $NetBSD: intr_distribute.h,v 1.1 2015/04/27 06:42:52 knakahara Exp $ */ + +/* + * Copyright (c) 2015 Internet Initiative Japan Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _X86_INTR_DISTRIBUTE_H_ +#define _X86_INTR_DISTRIBUTE_H_ + +#ifdef _KERNEL + +#include <sys/kcpuset.h> + +int intr_distribute(struct intrhand *, const kcpuset_t *, kcpuset_t *); + +#endif /* _KERNEL */ + +#endif /* !_X86_INTR_DISTRIBUTE_H_ */