diff --git a/distrib/sets/lists/base/mi b/distrib/sets/lists/base/mi index 6d619f3..dc0e5cd 100644 --- a/distrib/sets/lists/base/mi +++ b/distrib/sets/lists/base/mi @@ -1642,6 +1642,7 @@ ./usr/sbin/ifwatchd base-netutil-bin ./usr/sbin/inetd base-netutil-bin ./usr/sbin/installboot base-sysutil-bin +./usr/sbin/intrctl base-sysutil-bin ./usr/sbin/iopctl base-sysutil-bin ./usr/sbin/iostat base-sysutil-bin ./usr/sbin/ipfs base-ipf-bin ipfilter diff --git a/distrib/sets/lists/comp/mi b/distrib/sets/lists/comp/mi index ac7e973..32578546 100644 --- a/distrib/sets/lists/comp/mi +++ b/distrib/sets/lists/comp/mi @@ -3203,6 +3203,8 @@ ./usr/include/sys/gpio.h comp-c-include ./usr/include/sys/hash.h comp-c-include ./usr/include/sys/ieee754.h comp-c-include +./usr/include/sys/intr.h comp-c-include +./usr/include/sys/intrio.h comp-c-include ./usr/include/sys/inttypes.h comp-c-include ./usr/include/sys/ioccom.h comp-c-include ./usr/include/sys/ioctl.h comp-c-include diff --git a/distrib/sets/lists/debug/mi b/distrib/sets/lists/debug/mi index 7cb465a..e8ea44a 100644 --- a/distrib/sets/lists/debug/mi +++ b/distrib/sets/lists/debug/mi @@ -1058,6 +1058,7 @@ ./usr/libdata/debug/usr/sbin/ifwatchd.debug comp-netutil-debug debug ./usr/libdata/debug/usr/sbin/inetd.debug comp-netutil-debug debug ./usr/libdata/debug/usr/sbin/installboot.debug comp-sysutil-debug debug +./usr/libdata/debug/usr/sbin/intrctl.debug comp-sysutil-debug debug ./usr/libdata/debug/usr/sbin/iopctl.debug comp-sysutil-debug debug ./usr/libdata/debug/usr/sbin/iostat.debug comp-sysutil-debug debug ./usr/libdata/debug/usr/sbin/ipfs.debug comp-ipf-debug ipfilter,debug diff --git a/distrib/sets/lists/man/mi b/distrib/sets/lists/man/mi index 89b860b..7b90684 100644 --- a/distrib/sets/lists/man/mi +++ b/distrib/sets/lists/man/mi @@ -2545,6 +2545,7 @@ ./usr/share/man/cat8/inetd.0 man-netutil-catman .cat ./usr/share/man/cat8/init.0 man-sysutil-catman .cat ./usr/share/man/cat8/installboot.0 man-sysutil-catman .cat +./usr/share/man/cat8/intrctl.0 man-sysutil-catman .cat ./usr/share/man/cat8/intro.0 man-sys-catman .cat ./usr/share/man/cat8/iopctl.0 man-sysutil-catman .cat ./usr/share/man/cat8/iostat.0 man-sysutil-catman .cat @@ -5420,6 +5421,7 @@ ./usr/share/man/html8/inetd.html man-netutil-htmlman html ./usr/share/man/html8/init.html man-sysutil-htmlman html ./usr/share/man/html8/installboot.html man-sysutil-htmlman html +./usr/share/man/html8/intrctl.html man-sysutil-htmlman html ./usr/share/man/html8/intro.html man-sys-htmlman html ./usr/share/man/html8/iopctl.html man-sysutil-htmlman html ./usr/share/man/html8/iostat.html man-sysutil-htmlman html @@ -8441,6 +8443,7 @@ ./usr/share/man/man8/inetd.8 man-netutil-man .man ./usr/share/man/man8/init.8 man-sysutil-man .man ./usr/share/man/man8/installboot.8 man-sysutil-man .man +./usr/share/man/man8/intrctl.8 man-sysutil-man .man ./usr/share/man/man8/intro.8 man-sys-man .man ./usr/share/man/man8/iopctl.8 man-sysutil-man .man ./usr/share/man/man8/iostat.8 man-sysutil-man .man diff --git a/sbin/sysctl/sysctl.c b/sbin/sysctl/sysctl.c index 931b7c3..9a9f32e 100644 --- a/sbin/sysctl/sysctl.c +++ b/sbin/sysctl/sysctl.c @@ -197,6 +197,11 @@ static const struct handlespec { { "/kern/coredump/setid/mode", mode_bits, mode_bits, NULL }, { "/kern/drivers", kern_drivers, NULL, NULL }, + { "/kern/intr/list", printother, NULL, "intrctl" }, + { "/kern/intr/affinity", printother, NULL, "intrctl" }, + { "/kern/intr/intr", printother, NULL, "intrctl" }, + { "/kern/intr/nointr", printother, NULL, "intrctl" }, + { "/vm/vmmeter", printother, NULL, "vmstat' or 'systat" }, { "/vm/loadavg", vm_loadavg, NULL, NULL }, diff --git a/sys/arch/x86/include/intr.h b/sys/arch/x86/include/intr.h index f60ef25..fbed26c 100644 --- a/sys/arch/x86/include/intr.h +++ b/sys/arch/x86/include/intr.h @@ -86,13 +86,14 @@ struct intrsource { void *is_recurse; /* entry for spllower */ void *is_resume; /* entry for doreti */ lwp_t *is_lwp; /* for soft interrupts */ - struct evcnt is_evcnt; /* interrupt counter */ + struct evcnt is_evcnt; /* interrupt counter per cpu */ int is_flags; /* see below */ int is_type; /* level, edge */ int is_idtvec; int is_minlevel; char is_evname[32]; /* event counter name */ char is_intrid[INTRIDBUF]; /* intrid created by create_intrid() */ + char is_xname[INTRDEVNAMEBUF]; /* device names */ 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 */ @@ -185,6 +186,7 @@ typedef uint64_t intr_handle_t; void intr_default_setup(void); void x86_nmi(void); +void *intr_establish_xname(int, struct pic *, int, int, int, int (*)(void *), void *, bool, const char *); void *intr_establish(int, struct pic *, int, int, int, int (*)(void *), void *, bool); void intr_disestablish(struct intrhand *); void intr_add_pcibus(struct pcibus_attach_args *); diff --git a/sys/arch/x86/include/intr_distribute.h b/sys/arch/x86/include/intr_distribute.h index bae9fa0..c769e89 100644 --- a/sys/arch/x86/include/intr_distribute.h +++ b/sys/arch/x86/include/intr_distribute.h @@ -34,6 +34,7 @@ #include int intr_distribute(struct intrhand *, const kcpuset_t *, kcpuset_t *); +int intr_distribute_handler(const char *, const kcpuset_t *, kcpuset_t *); #endif /* _KERNEL */ diff --git a/sys/arch/x86/include/pci_machdep_common.h b/sys/arch/x86/include/pci_machdep_common.h index 521635b..a2443db 100644 --- a/sys/arch/x86/include/pci_machdep_common.h +++ b/sys/arch/x86/include/pci_machdep_common.h @@ -115,10 +115,14 @@ int pci_intr_map(const struct pci_attach_args *, const char *pci_intr_string(pci_chipset_tag_t, pci_intr_handle_t, char *, size_t); const struct evcnt *pci_intr_evcnt(pci_chipset_tag_t, pci_intr_handle_t); +void *pci_intr_establish_xname(pci_chipset_tag_t, pci_intr_handle_t, + int, int (*)(void *), void *, const char *); 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 *); +int pci_intr_distribute_handler(const char *, const kcpuset_t *, + kcpuset_t *); /* * If device drivers use MSI/MSI-X, they should use these API for INTx diff --git a/sys/arch/x86/pci/pci_intr_machdep.c b/sys/arch/x86/pci/pci_intr_machdep.c index 14e5e0f..5411aa7 100644 --- a/sys/arch/x86/pci/pci_intr_machdep.c +++ b/sys/arch/x86/pci/pci_intr_machdep.c @@ -275,8 +275,8 @@ pci_intr_setattr(pci_chipset_tag_t pc, pci_intr_handle_t *ih, } void * -pci_intr_establish(pci_chipset_tag_t pc, pci_intr_handle_t ih, - int level, int (*func)(void *), void *arg) +pci_intr_establish_xname(pci_chipset_tag_t pc, pci_intr_handle_t ih, + int level, int (*func)(void *), void *arg, const char *xname) { int pin, irq; struct pic *pic; @@ -320,8 +320,16 @@ pci_intr_establish(pci_chipset_tag_t pc, pci_intr_handle_t ih, } #endif - return intr_establish(irq, pic, pin, IST_LEVEL, level, func, arg, - mpsafe); + return intr_establish_xname(irq, pic, pin, IST_LEVEL, level, func, arg, + mpsafe, xname); +} + +void * +pci_intr_establish(pci_chipset_tag_t pc, pci_intr_handle_t ih, + int level, int (*func)(void *), void *arg) +{ + + return pci_intr_establish_xname(pc, ih, level, func, arg, "unknown"); } void @@ -349,6 +357,16 @@ pci_intr_distribute(void *cookie, const kcpuset_t *newset, kcpuset_t *oldset) return intr_distribute(cookie, newset, oldset); } +int +pci_intr_distribute_handler(const char *intrid, const kcpuset_t *newset, + kcpuset_t *oldset) +{ + + /* XXX Is pc_ov->ov_intr_distribute_handler required? */ + + return intr_distribute_handler(intrid, newset, oldset); +} + #if NIOAPIC > 0 static void x86_pci_intx_release(pci_chipset_tag_t pc, pci_intr_handle_t *pih) diff --git a/sys/arch/x86/x86/intr.c b/sys/arch/x86/x86/intr.c index 30761a6..7430bc2 100644 --- a/sys/arch/x86/x86/intr.c +++ b/sys/arch/x86/x86/intr.c @@ -152,6 +152,9 @@ __KERNEL_RCSID(0, "$NetBSD: intr.c,v 1.86 2015/06/23 10:00:13 msaitoh Exp $"); #include #include +#include +#include + #include #include @@ -224,6 +227,8 @@ static void intr_source_free(struct cpu_info *, int, struct pic *, int); static void intr_establish_xcall(void *, void *); static void intr_disestablish_xcall(void *, void *); +static const char *legacy_intr_string(int, char *, size_t, struct pic *); + static inline bool redzone_const_or_false(bool); static inline int redzone_const_or_zero(int); @@ -829,6 +834,18 @@ intr_findpic(int num) } /* + * Append device name to intrsource. If device A and device B share IRQ number, + * the device name of the interrupt id is "device A, device B". + */ +static void +intr_append_intrsource_xname(struct intrsource *isp, const char *xname) +{ + if (isp->is_xname[0] != '\0') + strncat(isp->is_xname, ", ", sizeof(isp->is_xname)); + strncat(isp->is_xname, xname, sizeof(isp->is_xname)); +} + +/* * Handle per-CPU component of interrupt establish. * * => caller (on initiating CPU) holds cpu_lock on our behalf @@ -882,8 +899,9 @@ intr_establish_xcall(void *arg1, void *arg2) } void * -intr_establish(int legacy_irq, struct pic *pic, int pin, int type, int level, - int (*handler)(void *), void *arg, bool known_mpsafe) +intr_establish_xname(int legacy_irq, struct pic *pic, int pin, int type, int level, + int (*handler)(void *), void *arg, bool known_mpsafe, + const char *xname) { struct intrhand **p, *q, *ih; struct cpu_info *ci; @@ -958,7 +976,7 @@ intr_establish(int legacy_irq, struct pic *pic, int pin, int type, int level, source->is_pin = pin; source->is_pic = pic; - + intr_append_intrsource_xname(source, xname); switch (source->is_type) { case IST_NONE: source->is_type = type; @@ -1047,6 +1065,14 @@ intr_establish(int legacy_irq, struct pic *pic, int pin, int type, int level, return (ih); } +void * +intr_establish(int legacy_irq, struct pic *pic, int pin, int type, int level, + int (*handler)(void *), void *arg, bool known_mpsafe) +{ + return intr_establish_xname(legacy_irq, pic, pin, type, + level, handler, arg, known_mpsafe, "unknown"); +} + /* * Called on bound CPU to handle intr_disestablish(). * @@ -1918,16 +1944,130 @@ intr_set_affinity(struct intrsource *isp, const kcpuset_t *cpuset) return err; } -int -intr_distribute(struct intrhand *ih, const kcpuset_t *newset, kcpuset_t *oldset) +static bool +intr_is_affinity_intrsource(struct intrsource *isp, const kcpuset_t *cpuset) +{ + struct cpu_info *ci; + + ci = isp->is_handlers->ih_cpu; + KASSERT(ci != NULL); + + return kcpuset_isset(cpuset, cpu_index(ci)); +} + +static struct intrhand * +intr_get_handler(const char *intrid) { struct intrsource *isp; - int ret, slot; + + KASSERT(mutex_owned(&cpu_lock)); + + isp = intr_get_io_intrsource(intrid); + if (isp == NULL) + return NULL; + + return isp->is_handlers; +} + +uint64_t +intr_get_count(const char *intrid, u_int cpu_idx) +{ + struct cpu_info *ci; + struct intrsource *isp; + struct intrhand *ih; + struct percpu_evcnt pep; + cpuid_t cpuid; + int i, slot; + + + mutex_enter(&cpu_lock); + ih = intr_get_handler(intrid); + mutex_exit(&cpu_lock); if (ih == NULL) - return EINVAL; + return 0; + + ci = cpu_lookup(cpu_idx); + cpuid = ci->ci_cpuid; + + slot = ih->ih_slot; + isp = ih->ih_cpu->ci_isources[slot]; + + for (i = 0; i < ncpu; i++) { + pep = isp->is_saved_evcnt[i]; + if (cpuid == pep.cpuid) { + if (isp->is_active_cpu == pep.cpuid) { + return isp->is_evcnt.ev_count; + } else { + return pep.count; + } + } + } + return 0; +} + +void +intr_get_assigned(const char *intrid, kcpuset_t *cpuset) +{ + struct cpu_info *ci; + struct intrhand *ih; + + mutex_enter(&cpu_lock); + ih = intr_get_handler(intrid); + mutex_exit(&cpu_lock); + + kcpuset_zero(cpuset); + + if (ih == NULL) + return; + + ci = ih->ih_cpu; + kcpuset_set(cpuset, cpu_index(ci)); +} + +void +intr_get_available(kcpuset_t *cpuset) +{ + CPU_INFO_ITERATOR cii; + struct cpu_info *ci; + + kcpuset_zero(cpuset); + for (CPU_INFO_FOREACH(cii, ci)) { + if ((ci->ci_schedstate.spc_flags & SPCF_NOINTR) == 0) { + kcpuset_set(cpuset, cpu_index(ci)); + } + } +} + +const char * +intr_get_devname(const char *intrid) +{ + struct intrsource *isp; + struct intrhand *ih; + int slot; mutex_enter(&cpu_lock); + ih = intr_get_handler(intrid); + mutex_exit(&cpu_lock); + + if (ih == NULL) + return ""; + + slot = ih->ih_slot; + isp = ih->ih_cpu->ci_isources[slot]; + + return isp->is_xname; +} + +static int +intr_distribute_nolock(struct intrhand *ih, const kcpuset_t *newset, + kcpuset_t *oldset) +{ + struct intrsource *isp; + int ret, slot; + + if (ih == NULL) + return EINVAL; slot = ih->ih_slot; isp = ih->ih_cpu->ci_isources[slot]; @@ -1938,7 +2078,108 @@ intr_distribute(struct intrhand *ih, const kcpuset_t *newset, kcpuset_t *oldset) ret = intr_set_affinity(isp, newset); + return ret; +} + +int +intr_distribute(struct intrhand *ih, const kcpuset_t *newset, kcpuset_t *oldset) +{ + int ret; + + mutex_enter(&cpu_lock); + ret = intr_distribute_nolock(ih, newset, oldset); + mutex_exit(&cpu_lock); + + return ret; +} + +int +intr_distribute_handler(const char *intrid, const kcpuset_t *newset, + kcpuset_t *oldset) +{ + int ret; + struct intrhand *ih; + + mutex_enter(&cpu_lock); + + ih = intr_get_handler(intrid); + if (ih == NULL) { + mutex_exit(&cpu_lock); + return ENOENT; + } + ret = intr_distribute_nolock(ih, newset, oldset); + mutex_exit(&cpu_lock); return ret; } + +int +intr_construct_intrids(const kcpuset_t *cpuset, char ***intrids, int *count) +{ + struct intrsource *isp; + char **ids; + int i; + + if (count == NULL) + return EINVAL; + + if (kcpuset_iszero(cpuset)) + return 0; + + *count = 0; + mutex_enter(&cpu_lock); + SIMPLEQ_FOREACH(isp, &io_interrupt_sources, is_list) { + if (intr_is_affinity_intrsource(isp, cpuset)) + (*count)++; + } + mutex_exit(&cpu_lock); + if (*count == 0) + return 0; + + ids = kmem_zalloc(sizeof(char*) * (*count), KM_SLEEP); + if (ids == NULL) + return ENOMEM; + for (i = 0; i < *count; i++) { + ids[i] = kmem_zalloc(INTRIDBUF, KM_SLEEP); + if (ids[i] == NULL) { + int j; + for (j = i - 1; j >= 0; j--) { + kmem_free(ids[j], INTRIDBUF); + } + kmem_free(ids, sizeof(char*) * (*count)); + return ENOMEM; + } + } + + i = 0; + mutex_enter(&cpu_lock); + SIMPLEQ_FOREACH(isp, &io_interrupt_sources, is_list) { + /* Ignore devices attached after counting "*count". */ + if (i >= *count) { + DPRINTF(("New devices are attached after counting.\n")); + break; + } + + if (!intr_is_affinity_intrsource(isp, cpuset)) + continue; + + strncpy(ids[i], isp->is_intrid, INTRIDBUF); + i++; + } + mutex_exit(&cpu_lock); + + *intrids = ids; + return 0; +} + +void +intr_destruct_intrids(char **intrids, int count) +{ + int i; + + for (i = 0; i < count; i++) + kmem_free(intrids[i], INTRIDBUF); + + kmem_free(intrids, sizeof(char*) * count); +} diff --git a/sys/conf/files b/sys/conf/files index bb80985..760eb18 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -1439,6 +1439,9 @@ file kern/kern_drvctl.c drvctl needs-flag defpseudo cpuctl defflag CPU_UCODE: firmload +# interrupt control +defpseudo intrctl + # pass-to-userspace transporter defpseudo putter file dev/putter/putter.c putter diff --git a/sys/kern/files.kern b/sys/kern/files.kern index 2399590..1c9eb36 100644 --- a/sys/kern/files.kern +++ b/sys/kern/files.kern @@ -131,6 +131,7 @@ file kern/subr_exec_fd.c kern file kern/subr_extent.c kern file kern/subr_hash.c kern file kern/subr_humanize.c kern +file kern/subr_intr.c kern file kern/subr_iostat.c kern file kern/subr_ipi.c kern file kern/subr_kcpuset.c kern diff --git a/sys/kern/kern_stub.c b/sys/kern/kern_stub.c index 5a0231c..737c2ce 100644 --- a/sys/kern/kern_stub.c +++ b/sys/kern/kern_stub.c @@ -147,7 +147,15 @@ __weak_alias(userconf_prompt, voidop); __weak_alias(kobj_renamespace, nullop); +__weak_alias(intr_get_count, eopnotsupp); +__weak_alias(intr_get_assigned, eopnotsupp); +__weak_alias(intr_get_available, eopnotsupp); +__weak_alias(intr_get_devname, eopnotsupp); +__weak_alias(intr_construct_intrids, eopnotsupp); +__weak_alias(intr_destruct_intrids, eopnotsupp); + __weak_alias(pci_intr_distribute, eopnotsupp); +__weak_alias(pci_intr_distribute_handler, eopnotsupp); /* * Scheduler activations system calls. These need to remain until libc's diff --git a/sys/kern/subr_intr.c b/sys/kern/subr_intr.c new file mode 100644 index 0000000..50b0217 --- /dev/null +++ b/sys/kern/subr_intr.c @@ -0,0 +1,475 @@ +/* $NetBSD$ */ + +/* + * 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. + */ + +#include +__KERNEL_RCSID(0, "$NetBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include + +#ifdef INTR_DEBUG +#define DPRINTF(msg) printf msg +#else +#define DPRINTF(msg) +#endif + +static struct intr_set kintr_set = { "\0", NULL, 0 }; + +#define UNSET_NOINTR_SHIELD 0 +#define SET_NOINTR_SHIELD 1 + +static void +intr_shield_xcall(void *arg1, void *arg2) +{ + struct cpu_info *ci; + struct schedstate_percpu *spc; + int s, shield; + + ci = arg1; + shield = (int)(intptr_t)arg2; + spc = &ci->ci_schedstate; + + s = splsched(); + if (shield == UNSET_NOINTR_SHIELD) + spc->spc_flags &= ~SPCF_NOINTR; + else if (shield == SET_NOINTR_SHIELD) + spc->spc_flags |= SPCF_NOINTR; + splx(s); +} + +/* + * Change SPCF_NOINTR flag of schedstate_percpu->spc_flags + */ +static int +intr_shield(u_int cpu_idx, int shield) +{ + struct cpu_info *ci; + struct schedstate_percpu *spc; + + KASSERT(mutex_owned(&cpu_lock)); + + ci = cpu_lookup(cpu_idx); + if (ci == NULL) + return EINVAL; + + spc = &ci->ci_schedstate; + if (shield == UNSET_NOINTR_SHIELD) { + if ((spc->spc_flags & SPCF_NOINTR) == 0) + return 0; + } else if (shield == SET_NOINTR_SHIELD) { + if ((spc->spc_flags & SPCF_NOINTR) != 0) + return 0; + } + + if (ci == curcpu() || !mp_online) { + intr_shield_xcall(ci, (void *)(intptr_t)shield); + } else { + uint64_t where; + where = xc_unicast(0, intr_shield_xcall, ci, + (void *)(intptr_t)shield, ci); + xc_wait(where); + } + + spc->spc_lastmod = time_second; + return 0; +} + +/* + * Move all assigned interrupts from "cpu_idx" to the other cpu as possible. + * The destination cpu is lowest cpuid of available cpus. + * If there are no available cpus, give up to move interrupts. + */ +static int +intr_avert_intr(u_int cpu_idx) +{ + kcpuset_t *cpuset; + char **ids; + int error, i, nids; + + kcpuset_create(&cpuset, true); + kcpuset_set(cpuset, cpu_idx); + + error = intr_construct_intrids(cpuset, &ids, &nids); + if (error) + return error; + if (nids == 0) + return 0; /* nothing to do */ + + intr_get_available(cpuset); + kcpuset_clear(cpuset, cpu_idx); + if (kcpuset_iszero(cpuset)) { + DPRINTF(("%s: no available cpu\n", __func__)); + return ENOENT; + } + + for (i = 0; i < nids; i++) { + error = pci_intr_distribute_handler(ids[i], cpuset, NULL); + if (error) + break; + } + + intr_destruct_intrids(ids, nids); + kcpuset_destroy(cpuset); + return error; +} + +/* + * Return actual intr_list_line size. intr_list_line size is variable by ncpu. + */ +static size_t +intr_list_line_size(void) +{ + + return sizeof(struct intr_list_line) + + sizeof(struct intr_list_line_cpu) * (ncpu - 1); +} + +static size_t +intr_list_size(void) +{ + size_t ilsize; + int error, nids; + char **ids; + + ilsize = 0; + + /* buffer header */ + ilsize += sizeof(struct intr_list); + + /* il_line body */ + error = intr_construct_intrids(kcpuset_running, &ids, &nids); + if (error) + return 0; + ilsize += intr_list_line_size() * nids; + + intr_destruct_intrids(ids, nids); + return ilsize; +} + +/* + * Set intrctl list to "data", and return list structure bytes. + * If error occured, return <0. + * If "data" == NULL, simply return list structure bytes. + */ +static int +intr_list(void *data, int length) +{ + struct intr_list *il; + struct intr_list_line *illine; + kcpuset_t *assigned, *avail; + size_t ilsize; + u_int cpu_idx; + int nids, intr_idx, ret, line_size; + char **ids; + + ilsize = intr_list_size(); + if (ilsize == 0) + return -ENOMEM; + + if (data == NULL) + return ilsize; + + if (length < ilsize) + return -ENOMEM; + + il = (struct intr_list *)data; + + illine = (struct intr_list_line *) + ((char *)il + sizeof(struct intr_list)); + il->il_lineoffset = (off_t)illine - (off_t)il; + + kcpuset_create(&avail, true); + intr_get_available(avail); + kcpuset_create(&assigned, true); + + ret = intr_construct_intrids(kcpuset_running, &ids, &nids); + if (ret != 0) { + DPRINTF(("%s: intr_construct_intrids() failed\n", + __func__)); + ret = -ret; + goto out; + } + + line_size = intr_list_line_size(); + /* ensure interrupts are not added after above intr_list_size(). */ + if (ilsize < sizeof(struct intr_list) + line_size * nids) { + intr_destruct_intrids(ids, nids); + DPRINTF(("%s: interrupts are added during execution.\n", + __func__)); + ret = -ENOMEM; + goto out; + } + + for (intr_idx = 0; intr_idx < nids; intr_idx++) { + strncpy(illine->ill_intrid, ids[intr_idx], INTRIDBUF); + strncpy(illine->ill_xname, intr_get_devname(ids[intr_idx]), + INTRDEVNAMEBUF); + + intr_get_assigned(ids[intr_idx], assigned); + for (cpu_idx = 0; cpu_idx < ncpu; cpu_idx++) { + struct intr_list_line_cpu *illcpu = + &illine->ill_cpu[cpu_idx]; + + illcpu->illc_assigned = + kcpuset_isset(assigned, cpu_idx) ? true : false; + illcpu->illc_count = + intr_get_count(ids[intr_idx], cpu_idx); + } + + illine = (struct intr_list_line *)((char *)illine + line_size); + } + + ret = ilsize; + il->il_version = INTR_LIST_VERSION; + il->il_ncpus = ncpu; + il->il_nintrs = nids; + il->il_linesize = line_size; + il->il_bufsize = ilsize; + + intr_destruct_intrids(ids, nids); + out: + kcpuset_destroy(assigned); + kcpuset_destroy(avail); + + return ret; +} + +/* + * "intrctl list" entry + */ +static int +intr_list_sysctl(SYSCTLFN_ARGS) +{ + int ret; + void *buf; + + if (oldlenp == NULL) + return EINVAL; + + if (oldp == NULL) { + ret = intr_list(NULL, 0); + if (ret < 0) + return -ret; + + *oldlenp = ret; + return 0; + } + + if (*oldlenp == 0) + return ENOMEM; + + buf = kmem_zalloc(*oldlenp, KM_SLEEP); + if (buf == NULL) + return ENOMEM; + + ret = intr_list(buf, *oldlenp); + if (ret < 0) + return -ret; + + return copyout(buf, oldp, *oldlenp); +} + +/* + * "intrctl affinity" entry + */ +static int +intr_set_affinity_sysctl(SYSCTLFN_ARGS) +{ + struct sysctlnode node; + struct intr_set *iset; + cpuset_t *ucpuset; + kcpuset_t *kcpuset; + int error; + + error = kauth_authorize_system(l->l_cred, KAUTH_SYSTEM_INTR, + KAUTH_REQ_SYSTEM_INTR_AFFINITY, NULL, NULL, NULL); + if (error) + return EPERM; + + node = *rnode; + iset = (struct intr_set *)node.sysctl_data; + + error = sysctl_lookup(SYSCTLFN_CALL(&node)); + if (error != 0 || newp == NULL) + return error; + + ucpuset = iset->cpuset; + kcpuset_create(&kcpuset, true); + kcpuset_copyin(ucpuset, kcpuset, iset->cpuset_size); + if (kcpuset_iszero(kcpuset)) { + kcpuset_destroy(kcpuset); + return EINVAL; + } + + error = pci_intr_distribute_handler(iset->intrid, kcpuset, NULL); + + kcpuset_destroy(kcpuset); + return error; +} + +/* + * "intrctl intr" entry + */ +static int +intr_intr_sysctl(SYSCTLFN_ARGS) +{ + struct sysctlnode node; + struct intr_set *iset; + cpuset_t *ucpuset; + kcpuset_t *kcpuset; + int error; + u_int cpu_idx; + + error = kauth_authorize_system(l->l_cred, KAUTH_SYSTEM_CPU, + KAUTH_REQ_SYSTEM_CPU_SETSTATE, NULL, NULL, NULL); + if (error) + return EPERM; + + node = *rnode; + iset = (struct intr_set *)node.sysctl_data; + + error = sysctl_lookup(SYSCTLFN_CALL(&node)); + if (error != 0 || newp == NULL) + return error; + + ucpuset = iset->cpuset; + kcpuset_create(&kcpuset, true); + kcpuset_copyin(ucpuset, kcpuset, iset->cpuset_size); + if (kcpuset_iszero(kcpuset)) { + kcpuset_destroy(kcpuset); + return EINVAL; + } + + cpu_idx = kcpuset_ffs(kcpuset) - 1; /* support one CPU only */ + + mutex_enter(&cpu_lock); + error = intr_shield(cpu_idx, UNSET_NOINTR_SHIELD); + mutex_exit(&cpu_lock); + + kcpuset_destroy(kcpuset); + return error; +} + +/* + * "intrctl nointr" entry + */ +static int +intr_nointr_sysctl(SYSCTLFN_ARGS) +{ + struct sysctlnode node; + struct intr_set *iset; + cpuset_t *ucpuset; + kcpuset_t *kcpuset; + int error; + u_int cpu_idx; + + error = kauth_authorize_system(l->l_cred, KAUTH_SYSTEM_CPU, + KAUTH_REQ_SYSTEM_CPU_SETSTATE, NULL, NULL, NULL); + if (error) + return EPERM; + + node = *rnode; + iset = (struct intr_set *)node.sysctl_data; + + error = sysctl_lookup(SYSCTLFN_CALL(&node)); + if (error != 0 || newp == NULL) + return error; + + ucpuset = iset->cpuset; + kcpuset_create(&kcpuset, true); + kcpuset_copyin(ucpuset, kcpuset, iset->cpuset_size); + if (kcpuset_iszero(kcpuset)) { + kcpuset_destroy(kcpuset); + return EINVAL; + } + + cpu_idx = kcpuset_ffs(kcpuset) - 1; /* support one CPU only */ + + mutex_enter(&cpu_lock); + error = intr_shield(cpu_idx, SET_NOINTR_SHIELD); + mutex_exit(&cpu_lock); + if (error) + return error; + + error = intr_avert_intr(cpu_idx); + + kcpuset_destroy(kcpuset); + return error; +} + +SYSCTL_SETUP(sysctl_intr_setup, "sysctl intr setup") +{ + const struct sysctlnode *node = NULL; + + sysctl_createv(clog, 0, NULL, &node, + CTLFLAG_PERMANENT, CTLTYPE_NODE, + "intr", SYSCTL_DESCR("Interrupt options"), + NULL, 0, NULL, 0, + CTL_KERN, CTL_CREATE, CTL_EOL); + + sysctl_createv(clog, 0, &node, NULL, + CTLFLAG_PERMANENT, CTLTYPE_STRUCT, + "list", SYSCTL_DESCR("intrctl list"), + intr_list_sysctl, 0, NULL, 0, + CTL_CREATE, CTL_EOL); + + sysctl_createv(clog, 0, &node, NULL, + CTLFLAG_PERMANENT|CTLFLAG_READWRITE, CTLTYPE_STRUCT, + "affinity", SYSCTL_DESCR("set affinity"), + intr_set_affinity_sysctl, 0, &kintr_set, sizeof(kintr_set), + CTL_CREATE, CTL_EOL); + + sysctl_createv(clog, 0, &node, NULL, + CTLFLAG_PERMANENT|CTLFLAG_READWRITE, CTLTYPE_STRUCT, + "intr", SYSCTL_DESCR("set intr"), + intr_intr_sysctl, 0, &kintr_set, sizeof(kintr_set), + CTL_CREATE, CTL_EOL); + + sysctl_createv(clog, 0, &node, NULL, + CTLFLAG_PERMANENT|CTLFLAG_READWRITE, CTLTYPE_STRUCT, + "nointr", SYSCTL_DESCR("set nointr"), + intr_nointr_sysctl, 0, &kintr_set, sizeof(kintr_set), + CTL_CREATE, CTL_EOL); +} diff --git a/sys/secmodel/suser/secmodel_suser.c b/sys/secmodel/suser/secmodel_suser.c index cbd6fcf..bbb15a0 100644 --- a/sys/secmodel/suser/secmodel_suser.c +++ b/sys/secmodel/suser/secmodel_suser.c @@ -443,6 +443,20 @@ secmodel_suser_system_cb(kauth_cred_t cred, kauth_action_t action, break; + case KAUTH_SYSTEM_INTR: + switch (req) { + case KAUTH_REQ_SYSTEM_INTR_AFFINITY: + if (isroot) + result = KAUTH_RESULT_ALLOW; + + break; + + default: + break; + } + + break; + default: break; } diff --git a/sys/sys/Makefile b/sys/sys/Makefile index 57e9a55..9bcd930 100644 --- a/sys/sys/Makefile +++ b/sys/sys/Makefile @@ -21,7 +21,7 @@ INCS= acct.h agpio.h aio.h ansi.h aout_mids.h ataio.h atomic.h audioio.h \ exec_coff.h exec_ecoff.h exec_elf.h exec_script.h extattr.h extent.h \ fcntl.h fd_set.h fdio.h featuretest.h file.h filedesc.h filio.h \ flashio.h float_ieee754.h fstypes.h gcq.h gmon.h gpio.h hash.h \ - ieee754.h inttypes.h ioccom.h ioctl.h ioctl_compat.h iostat.h ipc.h \ + ieee754.h intr.h intrio.h inttypes.h ioccom.h ioctl.h ioctl_compat.h iostat.h ipc.h \ joystick.h \ kcore.h kcpuset.h kgdb.h kmem.h ksem.h ksyms.h ktrace.h \ localedef.h lock.h lockf.h lua.h lwp.h lwpctl.h \ diff --git a/sys/sys/intr.h b/sys/sys/intr.h index b080a6e..4619a33 100644 --- a/sys/sys/intr.h +++ b/sys/sys/intr.h @@ -33,12 +33,15 @@ #define _SYS_INTR_H_ #define INTRIDBUF 64 +#define INTRDEVNAMEBUF 256 #ifdef _KERNEL #include struct cpu_info; +struct kcpuset; +typedef struct kcpuset kcpuset_t; /* Public interface. */ void *softint_establish(u_int, void (*)(void *), void *); @@ -59,6 +62,13 @@ void softint_trigger(uintptr_t); #endif void softint_dispatch(lwp_t *, int); +uint64_t intr_get_count(const char *, u_int); +void intr_get_assigned(const char *, kcpuset_t *); +void intr_get_available(kcpuset_t *); +const char *intr_get_devname(const char *); +int intr_construct_intrids(const kcpuset_t *, char ***, int *); +void intr_destruct_intrids(char **, int); + /* Flags for softint_establish(). */ #define SOFTINT_BIO 0x0000 #define SOFTINT_CLOCK 0x0001 diff --git a/sys/sys/intrio.h b/sys/sys/intrio.h new file mode 100644 index 0000000..4b01645 --- /dev/null +++ b/sys/sys/intrio.h @@ -0,0 +1,68 @@ +/* $NetBSD$ */ + +/* + * 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 _SYS_INTRIO_H_ +#define _SYS_INTRIO_H_ + +#include +#include +#include + +#define INTR_LIST_VERSION 1 + +struct intr_set { + char intrid[INTRIDBUF]; + cpuset_t *cpuset; + size_t cpuset_size; +}; + +struct intr_list_line_cpu { + bool illc_assigned; + uint64_t illc_count; +}; + +struct intr_list_line { + char ill_intrid[INTRIDBUF]; + char ill_xname[INTRDEVNAMEBUF]; + struct intr_list_line_cpu ill_cpu[1]; /* Array size is overwritten to ncpu. */ +}; + +struct intr_list { + int il_version; /* Version number of this struct. */ + int il_ncpus; + int il_nintrs; + size_t il_bufsize; + + size_t il_linesize; + off_t il_lineoffset; +/* + * struct intr_list_line il_lines[interrupt_num] must be followed here. + */ +}; + +#endif /* !_SYS_INTRIO_H_ */ diff --git a/sys/sys/kauth.h b/sys/sys/kauth.h index 6e53424..970afdc 100644 --- a/sys/sys/kauth.h +++ b/sys/sys/kauth.h @@ -111,6 +111,7 @@ enum { KAUTH_SYSTEM_LFS, KAUTH_SYSTEM_FS_EXTATTR, KAUTH_SYSTEM_FS_SNAPSHOT, + KAUTH_SYSTEM_INTR, }; /* @@ -156,6 +157,7 @@ enum kauth_system_req { KAUTH_REQ_SYSTEM_LFS_FCNTL, KAUTH_REQ_SYSTEM_MOUNT_UMAP, KAUTH_REQ_SYSTEM_MOUNT_DEVICE, + KAUTH_REQ_SYSTEM_INTR_AFFINITY, }; /* diff --git a/usr.sbin/Makefile b/usr.sbin/Makefile index 752faca..702944c 100644 --- a/usr.sbin/Makefile +++ b/usr.sbin/Makefile @@ -11,7 +11,7 @@ SUBDIR= ac accton acpitools altq apm apmd arp \ flashctl fssconfig fusermount fwctl \ gpioctl grfconfig gspa \ hdaudioctl \ - i2cscan ifwatchd inetd installboot iopctl iostat ipwctl irdaattach \ + i2cscan ifwatchd inetd installboot intrctl iopctl iostat ipwctl irdaattach \ isdn isibootd iteconfig iwictl \ kgmon \ lastlogin ldpd link lmcconfig lockstat lpr \ diff --git a/usr.sbin/intrctl/Makefile b/usr.sbin/intrctl/Makefile new file mode 100644 index 0000000..b684086 --- /dev/null +++ b/usr.sbin/intrctl/Makefile @@ -0,0 +1,9 @@ +# $NetBSD$ + +.include + +PROG= intrctl +MAN= intrctl.8 +SRCS= intrctl.c intrctl_io.c + +.include diff --git a/usr.sbin/intrctl/intrctl.8 b/usr.sbin/intrctl/intrctl.8 new file mode 100644 index 0000000..a493711 --- /dev/null +++ b/usr.sbin/intrctl/intrctl.8 @@ -0,0 +1,72 @@ +.\" $NetBSD$ +.\" +.\" 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 August 19, 2014 +.Dt INTRCTL 8 +.Os +.Sh NAME +.Nm intrctl +.Nd program to control interrupts +.Sh SYNOPSIS +.Nm intrctl +.Ar command +.Op Ar options +.Sh DESCRIPTION +The +.Nm +command can be used to control and inspect the state of intrerupts in the system. +.Pp +The first argument, +.Ar command , +specifies the action to take. +Valid commands are: +.Bl -tag -width XofflineXcpunoX +.It list +for each IRQ in the system, display interrupt counts per CPU. +.It affinity Fl c Ar cpu_index Fl i Ar irq +set affinity +.Ar irq +interrupt to +.Ar cpu_index. +.It intr Fl c Ar cpu_index +enable interrupts to set affinity to +.Ar cpu_index. +.It nointr Fl c Ar cpu_index +disable interrupts to set affinity to +.Ar cpu_index. +.El +.Sh FILES +.Bl -tag -width /dev/intrctl -compact +.It Pa /dev/intrctl +control interrupt +.Ed +.Sh SEE ALSO +.Xr cpuctl 8 +.Sh HISTORY +The +.Nm +command first appeared in +.Nx X.X . diff --git a/usr.sbin/intrctl/intrctl.c b/usr.sbin/intrctl/intrctl.c new file mode 100644 index 0000000..f422003 --- /dev/null +++ b/usr.sbin/intrctl/intrctl.c @@ -0,0 +1,271 @@ +/* $NetBSD$ */ + +/* + * 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. + */ + +#include +__RCSID("$NetBSD$"); + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "intrctl_io.h" + +__dead static void usage(void); + +int verbose; + +static void intr_list(int, char **); +static void intr_affinity(int, char **); +static void intr_intr(int, char **); +static void intr_nointr(int, char **); + +static struct cmdtab { + const char *label; + void (*func)(int, char **); +} const intr_cmdtab[] = { + { "list", intr_list }, + { "affinity", intr_affinity }, + { "intr", intr_intr }, + { "nointr", intr_nointr }, + { NULL, NULL }, +}; + +int +main(int argc, char **argv) +{ + const struct cmdtab *ct; + char *cmdname; + + if (argc < 2) + usage(); + + cmdname = argv[1]; + argv += 1; + argc -= 1; + + for (ct = intr_cmdtab; ct->label != NULL; ct++) { + if (strcmp(cmdname, ct->label) == 0) { + break; + } + } + if (ct->label == NULL) + errx(EXIT_FAILURE, "unknown command ``%s''", cmdname); + + (*ct->func)(argc, argv); + exit(EXIT_SUCCESS); + /* NOTREACHED */ +} + +static void +usage(void) +{ + const char *progname = getprogname(); + + fprintf(stderr, "usage: %s list\n", progname); + fprintf(stderr, " %s affinity -i interrupt_name -c cpu_index\n", progname); + fprintf(stderr, " %s intr -c cpu_index\n", progname); + fprintf(stderr, " %s nointr -c cpu_index\n", progname); + exit(EXIT_FAILURE); + /* NOTREACHED */ +} + +static int intrctl_io_alloc_retry_count = 4; + +static void +intr_list(int argc, char **argv) +{ + struct intr_list_line *illine; + int i, ncpus; + void *handle; + + handle = intrctl_io_alloc(intrctl_io_alloc_retry_count); + if (handle == NULL) + err(EXIT_FAILURE, "intrctl_io_alloc"); + + /* header */ + ncpus = intrctl_io_ncpus(handle); + printf("interrupt id\t"); + for (i = 0; i < ncpus; i++) { + printf(" CPU#%02u\t", i); + } + printf("device name(s)\n"); + + /* body */ + illine = intrctl_io_firstline(handle); + for (; illine != NULL; illine = intrctl_io_nextline(handle, illine)) { + printf("%s\t", illine->ill_intrid); + for (i = 0; i < ncpus; i++) { + struct intr_list_line_cpu *illc = &illine->ill_cpu[i]; + printf("%8" PRIu64 "%c\t", illc->illc_count, + illc->illc_assigned ? '*' : ' '); + } + + printf("%s\n", illine->ill_xname); + } + + intrctl_io_free(handle); +} + +static void +intr_affinity(int argc, char **argv) +{ + struct intr_set iset; + cpuset_t *cpuset; + unsigned long index; + int ch, error; + + index = ULONG_MAX; + memset(&iset.intrid, 0, sizeof(iset.intrid)); + + while ((ch = getopt(argc, argv, "c:i:")) != -1) { + switch (ch) { + case 'c': + index = strtoul(optarg, NULL, 10); + break; + case 'i': + if (strnlen(optarg, ARG_MAX) > INTRIDBUF) + usage(); + strlcpy(iset.intrid, optarg, INTRIDBUF); + break; + default: + usage(); + } + } + + if (iset.intrid[0] == '\0' || index == ULONG_MAX) + usage(); + + if (index >= (u_long)sysconf(_SC_NPROCESSORS_CONF)) + err(EXIT_FAILURE, "invalid cpu index"); + + cpuset = cpuset_create(); + if (cpuset == NULL) + err(EXIT_FAILURE, "create_cpuset()"); + + cpuset_zero(cpuset); + cpuset_set(index, cpuset); + iset.cpuset = cpuset; + iset.cpuset_size = cpuset_size(cpuset); + error = sysctlbyname("kern.intr.affinity", NULL, NULL, &iset, sizeof(iset)); + cpuset_destroy(cpuset); + if (error < 0) + err(EXIT_FAILURE, "sysctl kern.intr.affinity"); +} + +static void +intr_intr(int argc, char **argv) +{ + struct intr_set iset; + cpuset_t *cpuset; + unsigned long index; + int ch, error; + + index = ULONG_MAX; + + while ((ch = getopt(argc, argv, "c:")) != -1) { + switch (ch) { + case 'c': + index = strtoul(optarg, NULL, 10); + break; + default: + usage(); + } + } + + if (index == ULONG_MAX) + usage(); + + if (index >= (u_long)sysconf(_SC_NPROCESSORS_CONF)) + err(EXIT_FAILURE, "invalid cpu index"); + + cpuset = cpuset_create(); + if (cpuset == NULL) + err(EXIT_FAILURE, "create_cpuset()"); + + cpuset_zero(cpuset); + cpuset_set(index, cpuset); + iset.cpuset = cpuset; + iset.cpuset_size = cpuset_size(cpuset); + error = sysctlbyname("kern.intr.intr", NULL, NULL, &iset, sizeof(iset)); + cpuset_destroy(cpuset); + if (error < 0) + err(EXIT_FAILURE, "sysctl kern.intr.intr"); +} + +static void +intr_nointr(int argc, char **argv) +{ + struct intr_set iset; + cpuset_t *cpuset; + unsigned long index; + int ch, error; + + index = ULONG_MAX; + + while ((ch = getopt(argc, argv, "c:")) != -1) { + switch (ch) { + case 'c': + index = strtoul(optarg, NULL, 10); + break; + default: + usage(); + } + } + + if (index == ULONG_MAX) + usage(); + + if (index >= (u_long)sysconf(_SC_NPROCESSORS_CONF)) + err(EXIT_FAILURE, "invalid cpu index"); + + cpuset = cpuset_create(); + if (cpuset == NULL) + err(EXIT_FAILURE, "create_cpuset()"); + + cpuset_zero(cpuset); + cpuset_set(index, cpuset); + iset.cpuset = cpuset; + iset.cpuset_size = cpuset_size(cpuset); + error = sysctlbyname("kern.intr.nointr", NULL, NULL, &iset, sizeof(iset)); + cpuset_destroy(cpuset); + if (error < 0) + err(EXIT_FAILURE, "sysctl kern.intr.nointr"); +} diff --git a/usr.sbin/intrctl/intrctl_io.c b/usr.sbin/intrctl/intrctl_io.c new file mode 100644 index 0000000..da107e3 --- /dev/null +++ b/usr.sbin/intrctl/intrctl_io.c @@ -0,0 +1,131 @@ +/* $NetBSD$ */ + +/* + * 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. + */ + +#include +__RCSID("$NetBSD$"); + +#include +#include + +#include +#include +#include + +#include "intrctl_io.h" + +/* + * To support increasing the number of interrupts (devices are dynamically + * attached), retry sysctl(3) "retry" times. + */ +void * +intrctl_io_alloc(int retry) +{ + size_t buf_size; + int i, error; + void *buf; + + error = sysctlbyname("kern.intr.list", NULL, &buf_size, NULL, 0); + if (error < 0) { + return NULL; + } + + buf = malloc(buf_size); + if (buf == NULL) { + return NULL; + } + + for (i = 0; i < retry; i++) { + error = sysctlbyname("kern.intr.list", buf, &buf_size, NULL, 0); + if (error >= 0) + return buf; + else if (error == -ENOMEM) { + void *temp; + + temp = realloc(buf, buf_size); + if (temp == NULL) { + free(buf); + return NULL; + } + buf = temp; + } else { + free(buf); + return NULL; + } + } + return NULL; +} + +void +intrctl_io_free(void *handle) +{ + + free(handle); +} + +int +intrctl_io_ncpus(void *handle) +{ + struct intr_list *list = handle; + + return list->il_ncpus; +} + +int +intrctl_io_nintrs(void *handle) +{ + struct intr_list *list = handle; + + return list->il_nintrs; +} + +struct intr_list_line * +intrctl_io_firstline(void *handle) +{ + struct intr_list *list = handle; + + return (struct intr_list_line *)((char *)list + list->il_lineoffset); +} + +struct intr_list_line * +intrctl_io_nextline(void *handle, struct intr_list_line *cur) +{ + struct intr_list *list; + struct intr_list_line *next; + size_t line_size; + char *buf_end; + + list = handle; + buf_end = (char *)list + list->il_bufsize; + + line_size = list->il_linesize; + next = (struct intr_list_line *)((char *)cur + line_size); + if ((char *)next >= buf_end) + return NULL; + + return next; +} diff --git a/usr.sbin/intrctl/intrctl_io.h b/usr.sbin/intrctl/intrctl_io.h new file mode 100644 index 0000000..b6ff88a --- /dev/null +++ b/usr.sbin/intrctl/intrctl_io.h @@ -0,0 +1,44 @@ +/* $NetBSD$ */ + +/* + * 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 _INTRCTL_INTRCTL_IO_H_ +#define _INTRCTL_INTRCTL_IO_H_ + +#include +__RCSID("$NetBSD$"); + +#include + +void *intrctl_io_alloc(int); +void intrctl_io_free(void *); +int intrctl_io_ncpus(void *); +int intrctl_io_nintrs(void *); +struct intr_list_line *intrctl_io_firstline(void *); +struct intr_list_line *intrctl_io_nextline(void *, struct intr_list_line *); + +#endif /* !_INTRCTL_INTRCTL_IO_H_ */