Page MenuHomeFreeBSD

D10435.id27945.diff
No OneTemporary

D10435.id27945.diff

Index: etc/defaults/rc.conf
===================================================================
--- etc/defaults/rc.conf
+++ etc/defaults/rc.conf
@@ -682,6 +682,9 @@
iovctl_files="" # Config files for iovctl(8)
+irqrebalance_enable="NO" # Balance IRQs dynamically
+irqrebalance_period="" # Rebalance period (seconds) (defaults to 60)
+
##############################################################
### Jail Configuration (see rc.conf(5) manual page) ##########
##############################################################
Index: etc/rc.d/irqrebalance
===================================================================
--- /dev/null
+++ etc/rc.d/irqrebalance
@@ -0,0 +1,31 @@
+#!/bin/sh
+#
+# $FreeBSD$
+#
+
+# PROVIDE: irqrebalance
+
+. /etc/rc.subr
+
+name="irqrebalance"
+desc="Dynamically rebalance interrupts across cores depending on load"
+rcvar="irqrebalance_enable"
+command="/libexec/${name}"
+start_precmd="irqrebalance_precmd"
+command_args="&"
+
+irqrebalance_precmd()
+{
+
+ case "${irqrebalance_period}" in
+ '')
+ rc_flags="-f 60"
+ ;;
+ *)
+ rc_flags="-f ${irqrebalance_period}"
+ ;;
+ esac
+}
+
+load_rc_config $name
+run_rc_command "$1"
Index: libexec/Makefile
===================================================================
--- libexec/Makefile
+++ libexec/Makefile
@@ -9,6 +9,7 @@
${_comsat} \
${_dma} \
getty \
+ ${_irqrebalance} \
${_mail.local} \
${_makewhatis.local} \
${_mknetid} \
@@ -58,6 +59,10 @@
_dma= dma
.endif
+.if ${MK_IRQREBALANCE} != "no"
+_irqrebalance= irqrebalance
+.endif
+
.if ${MK_NIS} != "no"
_mknetid= mknetid
_ypxfr= ypxfr
Index: libexec/irqrebalance/Makefile
===================================================================
--- /dev/null
+++ libexec/irqrebalance/Makefile
@@ -0,0 +1,9 @@
+# $FreeBSD$
+
+.include <src.opts.mk>
+
+PROG= irqrebalance
+MAN=
+SRCS= irqrebalance.c
+
+.include <bsd.prog.mk>
Index: libexec/irqrebalance/irqrebalance.c
===================================================================
--- /dev/null
+++ libexec/irqrebalance/irqrebalance.c
@@ -0,0 +1,222 @@
+/*
+ * Copyright (c) 2017 Dell EMC Isilon
+ *
+ * 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 AUTHOR 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 AUTHOR 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.
+ *
+ * $FreeBSD $
+ */
+
+#include <sys/param.h>
+#include <sys/cpuset.h>
+#include <sys/sysctl.h>
+
+#include <err.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+struct intr_src {
+ const char *is_name;
+ unsigned long is_count;
+ int is_irq;
+};
+
+static cpuset_t cpus;
+static struct intr_src *intr_sources;
+static char *intrnames;
+
+static void enumerate_irqs(void);
+static void irqshuffle(void);
+
+/*
+ * irqrebalance options:
+ * -f period
+ * Rebalance every N seconds.
+ */
+int
+main(int argc, char **argv)
+{
+ int c;
+ time_t period;
+
+ while ((c = getopt(argc, argv, "f:")) != -1) {
+ switch (c) {
+ case 'f':
+ period = atol(optarg);
+ if (period <= 0)
+ return (1);
+ break;
+ default:
+ return (1);
+ }
+ }
+
+ enumerate_irqs();
+ while (true) {
+ irqshuffle();
+ sleep(period);
+ }
+ /* NOTREACHED */
+ return (0);
+}
+
+static size_t
+read_intrcnts(unsigned long **intrcnts)
+{
+ size_t intrcntlen;
+ int rc;
+
+ for (*intrcnts = NULL, intrcntlen = 1024; ; intrcntlen *= 2) {
+ *intrcnts = reallocf(*intrcnts, intrcntlen);
+ if (*intrcnts == NULL)
+ err(1, "reallocf");
+ rc = sysctlbyname("hw.intrcnt", *intrcnts, &intrcntlen, NULL, 0);
+ if (rc == 0)
+ break;
+ else if (rc != ENOMEM)
+ err(1, "sysctl");
+ }
+
+ return (intrcntlen / sizeof(unsigned long));
+}
+
+static void
+enumerate_irqs(void)
+{
+ size_t inamlen, intrcnt, i;
+ unsigned long *counts;
+ const char *name;
+ int rc;
+
+ for (intrnames = NULL, inamlen = 1024; ; inamlen *= 2) {
+ if ((intrnames = reallocf(intrnames, inamlen)) == NULL)
+ err(1, "reallocf");
+ rc = sysctlbyname("hw.intrnames", intrnames, &inamlen, NULL, 0);
+ if (rc == 0)
+ break;
+ else if (rc != ENOMEM)
+ err(1, "sysctl");
+ }
+
+ intrcnt = read_intrcnts(&counts);
+ free(counts);
+
+ intr_sources = calloc(intrcnt, sizeof(*intr_sources));
+ if (intr_sources == NULL)
+ err(1, "calloc");
+
+ for (i = 0, name = intrnames; i < intrcnt; i++) {
+ if (name[0] != '\0')
+ intr_sources[i].is_name = strdup(name);
+ /* XXX */
+ if (strncmp(name, "irq", 3) != 0) {
+ rc = sscanf(name, "irq%d:", &intr_sources[i].is_irq);
+ if (rc < 1)
+ intr_sources[i].is_irq = -1;
+ } else
+ intr_sources[i].is_irq = -1;
+ name += strlen(name) + 1;
+ }
+
+ rc = cpuset_getaffinity(CPU_LEVEL_ROOT, CPU_WHICH_CPUSET, -1,
+ sizeof(cpus), &cpus);
+ if (rc != 0)
+ err(1, "cpuset_getaffinity");
+}
+
+static int
+intrcmp(const void *one, const void *two)
+{
+ const struct intr_src *i1, *i2;
+
+ i1 = one;
+ i2 = two;
+ if (i1->is_count != 0 && i2->is_count != 0) {
+ if (i1->is_count < i2->is_count)
+ return (-1);
+ else if (i1->is_count == i2->is_count)
+ return (0);
+ return (1);
+ }
+
+ if (i1->is_count != 0)
+ return (1);
+ else if (i2->is_count != 0)
+ return (-1);
+ return (0);
+}
+
+static size_t
+nextcpu(const cpuset_t *allcpus, size_t idx, cpuset_t *output)
+{
+
+ CPU_ZERO(output);
+ while (true) {
+ if (CPU_ISSET(idx, allcpus)) {
+ CPU_SET(idx, output);
+ break;
+ }
+ idx = (idx + 1) % CPU_SETSIZE;
+ }
+ return ((idx + 1) % CPU_SETSIZE);
+}
+
+static void
+irqshuffle(void)
+{
+ const struct intr_src *isrc;
+ unsigned long *intrcnts;
+ size_t current_cpu;
+ ssize_t i, nintrs;
+ cpuset_t mask;
+ int rc;
+
+ nintrs = read_intrcnts(&intrcnts);
+
+ for (i = 0; i < nintrs; i++)
+ intr_sources[i].is_count = intrcnts[i];
+
+ qsort(intr_sources, nintrs, sizeof(*intr_sources), intrcmp);
+
+ /*
+ * Scan from the same location to avoid moving in the common case.
+ */
+ current_cpu = 0;
+
+ for (i = nintrs - 1; i >= 0; i--) {
+ isrc = &intr_sources[i];
+ current_cpu = nextcpu(&cpus, current_cpu, &mask);
+
+ if (isrc->is_irq < 0)
+ continue;
+
+ /* XXX Differentiate managed and unmanaged irqs? */
+ rc = cpuset_setaffinity(CPU_LEVEL_WHICH, CPU_WHICH_IRQ_ONLY,
+ isrc->is_irq, sizeof(mask), &mask);
+ if (rc != 0)
+ err(1, "cpuset_setaffinity");
+ }
+}
Index: share/mk/src.opts.mk
===================================================================
--- share/mk/src.opts.mk
+++ share/mk/src.opts.mk
@@ -112,6 +112,7 @@
INETD \
IPFILTER \
IPFW \
+ IRQREBALANCE \
ISCSI \
JAIL \
KDUMP \
Index: sys/kern/kern_cpuset.c
===================================================================
--- sys/kern/kern_cpuset.c
+++ sys/kern/kern_cpuset.c
@@ -1115,6 +1115,8 @@
case CPU_WHICH_JAIL:
break;
case CPU_WHICH_IRQ:
+ case CPU_WHICH_IRQ_ONLY:
+ case CPU_WHICH_ITHREAD:
case CPU_WHICH_DOMAIN:
error = EINVAL;
goto out;
@@ -1145,7 +1147,9 @@
CPU_COPY(&set->cs_mask, mask);
break;
case CPU_WHICH_IRQ:
- error = intr_getaffinity(id, mask);
+ case CPU_WHICH_IRQ_ONLY:
+ case CPU_WHICH_ITHREAD:
+ error = intr_getaffinity(id, which, mask);
break;
case CPU_WHICH_DOMAIN:
if (id < 0 || id >= MAXMEMDOM)
@@ -1239,6 +1243,8 @@
case CPU_WHICH_JAIL:
break;
case CPU_WHICH_IRQ:
+ case CPU_WHICH_IRQ_ONLY:
+ case CPU_WHICH_ITHREAD:
case CPU_WHICH_DOMAIN:
error = EINVAL;
goto out;
@@ -1268,7 +1274,9 @@
}
break;
case CPU_WHICH_IRQ:
- error = intr_setaffinity(id, mask);
+ case CPU_WHICH_IRQ_ONLY:
+ case CPU_WHICH_ITHREAD:
+ error = intr_setaffinity(id, which, mask);
break;
default:
error = EINVAL;
Index: sys/kern/kern_intr.c
===================================================================
--- sys/kern/kern_intr.c
+++ sys/kern/kern_intr.c
@@ -287,13 +287,11 @@
/*
* 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
+ * platforms this request will fail. Using a cpu id of NOCPU unbinds
* the interrupt event.
*/
-int
-intr_event_bind(struct intr_event *ie, int cpu)
+static int
+_intr_event_bind(struct intr_event *ie, int cpu, int bindirq, int bindithread)
{
lwpid_t id;
int error;
@@ -313,35 +311,75 @@
* If we have any ithreads try to set their mask first to verify
* permissions, etc.
*/
- mtx_lock(&ie->ie_lock);
- if (ie->ie_thread != NULL) {
- id = ie->ie_thread->it_thread->td_tid;
- mtx_unlock(&ie->ie_lock);
- error = cpuset_setithread(id, cpu);
- if (error)
- return (error);
- } else
- mtx_unlock(&ie->ie_lock);
- error = ie->ie_assign_cpu(ie->ie_source, cpu);
- if (error) {
+ if (bindithread) {
mtx_lock(&ie->ie_lock);
if (ie->ie_thread != NULL) {
- cpu = ie->ie_cpu;
id = ie->ie_thread->it_thread->td_tid;
mtx_unlock(&ie->ie_lock);
- (void)cpuset_setithread(id, cpu);
+ error = cpuset_setithread(id, cpu);
+ if (error)
+ return (error);
} else
mtx_unlock(&ie->ie_lock);
+ }
+ if (bindirq)
+ error = ie->ie_assign_cpu(ie->ie_source, cpu);
+ if (error) {
+ if (bindithread) {
+ mtx_lock(&ie->ie_lock);
+ if (ie->ie_thread != NULL) {
+ cpu = ie->ie_cpu;
+ id = ie->ie_thread->it_thread->td_tid;
+ mtx_unlock(&ie->ie_lock);
+ (void)cpuset_setithread(id, cpu);
+ } else
+ mtx_unlock(&ie->ie_lock);
+ }
return (error);
}
- mtx_lock(&ie->ie_lock);
- ie->ie_cpu = cpu;
- mtx_unlock(&ie->ie_lock);
+ if (bindirq) {
+ mtx_lock(&ie->ie_lock);
+ ie->ie_cpu = cpu;
+ mtx_unlock(&ie->ie_lock);
+ }
return (error);
}
+/*
+ * Bind an interrupt event to the specified CPU. For supported platforms, any
+ * associated ithreads as well as the primary interrupt context will be bound
+ * to the specificed CPU.
+ */
+int
+intr_event_bind(struct intr_event *ie, int cpu)
+{
+
+ return (_intr_event_bind(ie, cpu, 1, 1));
+}
+
+/*
+ * Bind an interrupt event to the specified CPU, but do not bind associated
+ * ithreads.
+ */
+int
+intr_event_bind_irqonly(struct intr_event *ie, int cpu)
+{
+
+ return (_intr_event_bind(ie, cpu, 1, 0));
+}
+
+/*
+ * Bind an interrupt event's ithread to the specified CPU.
+ */
+int
+intr_event_bind_ithread(struct intr_event *ie, int cpu)
+{
+
+ return (_intr_event_bind(ie, cpu, 0, 1));
+}
+
static struct intr_event *
intr_lookup(int irq)
{
@@ -358,7 +396,7 @@
}
int
-intr_setaffinity(int irq, void *m)
+intr_setaffinity(int irq, int mode, void *m)
{
struct intr_event *ie;
cpuset_t *mask;
@@ -382,26 +420,62 @@
ie = intr_lookup(irq);
if (ie == NULL)
return (ESRCH);
- return (intr_event_bind(ie, cpu));
+ switch (mode) {
+ case CPU_WHICH_IRQ:
+ return (intr_event_bind(ie, cpu));
+ case CPU_WHICH_IRQ_ONLY:
+ return (intr_event_bind_irqonly(ie, cpu));
+ case CPU_WHICH_ITHREAD:
+ return (intr_event_bind_ithread(ie, cpu));
+ default:
+ return (EINVAL);
+ }
}
int
-intr_getaffinity(int irq, void *m)
+intr_getaffinity(int irq, int mode, void *m)
{
struct intr_event *ie;
+ struct thread *td;
+ struct proc *p;
cpuset_t *mask;
+ lwpid_t id;
+ int error;
mask = m;
ie = intr_lookup(irq);
if (ie == NULL)
return (ESRCH);
+
+ error = 0;
CPU_ZERO(mask);
- mtx_lock(&ie->ie_lock);
- if (ie->ie_cpu == NOCPU)
- CPU_COPY(cpuset_root, mask);
- else
- CPU_SET(ie->ie_cpu, mask);
- mtx_unlock(&ie->ie_lock);
+ switch (mode) {
+ case CPU_WHICH_IRQ:
+ case CPU_WHICH_IRQ_ONLY:
+ mtx_lock(&ie->ie_lock);
+ if (ie->ie_cpu == NOCPU)
+ CPU_COPY(cpuset_root, mask);
+ else
+ CPU_SET(ie->ie_cpu, mask);
+ mtx_unlock(&ie->ie_lock);
+ break;
+ case CPU_WHICH_ITHREAD:
+ mtx_lock(&ie->ie_lock);
+ if (ie->ie_thread == NULL) {
+ mtx_unlock(&ie->ie_lock);
+ CPU_COPY(cpuset_root, mask);
+ } else {
+ id = ie->ie_thread->it_thread->td_tid;
+ mtx_unlock(&ie->ie_lock);
+ error = cpuset_which(CPU_WHICH_TID, id, &p, &td, NULL);
+ if (error != 0)
+ return (error);
+ CPU_COPY(&td->td_cpuset->cs_mask, mask);
+ PROC_UNLOCK(p);
+ }
+ default:
+ return (EINVAL);
+ }
return (0);
}
Index: sys/kern/subr_gtaskqueue.c
===================================================================
--- sys/kern/subr_gtaskqueue.c
+++ sys/kern/subr_gtaskqueue.c
@@ -679,7 +679,7 @@
CPU_ZERO(&mask);
CPU_SET(qgroup->tqg_queue[qid].tgc_cpu, &mask);
mtx_unlock(&qgroup->tqg_lock);
- intr_setaffinity(irq, &mask);
+ intr_setaffinity(irq, CPU_WHICH_IRQ, &mask);
} else
mtx_unlock(&qgroup->tqg_lock);
}
@@ -698,7 +698,7 @@
CPU_ZERO(&mask);
CPU_SET(cpu, &mask);
- intr_setaffinity(gtask->gt_irq, &mask);
+ intr_setaffinity(gtask->gt_irq, CPU_WHICH_IRQ, &mask);
mtx_lock(&qgroup->tqg_lock);
}
@@ -745,7 +745,7 @@
CPU_ZERO(&mask);
CPU_SET(cpu, &mask);
if (irq != -1 && tqg_smp_started)
- intr_setaffinity(irq, &mask);
+ intr_setaffinity(irq, CPU_WHICH_IRQ, &mask);
return (0);
}
@@ -779,7 +779,7 @@
CPU_SET(cpu, &mask);
if (irq != -1)
- intr_setaffinity(irq, &mask);
+ intr_setaffinity(irq, CPU_WHICH_IRQ, &mask);
return (0);
}
Index: sys/sys/cpuset.h
===================================================================
--- sys/sys/cpuset.h
+++ sys/sys/cpuset.h
@@ -83,6 +83,8 @@
#define CPU_WHICH_IRQ 4 /* Specifies an irq #. */
#define CPU_WHICH_JAIL 5 /* Specifies a jail id. */
#define CPU_WHICH_DOMAIN 6 /* Specifies a NUMA domain id. */
+#define CPU_WHICH_IRQ_ONLY 7 /* Specifies an irq # (not ithread). */
+#define CPU_WHICH_ITHREAD 8 /* Specifies an irq's ithread. */
/*
* Reserved cpuset identifiers.
Index: sys/sys/interrupt.h
===================================================================
--- sys/sys/interrupt.h
+++ sys/sys/interrupt.h
@@ -162,6 +162,8 @@
driver_filter_t filter, driver_intr_t handler, void *arg,
u_char pri, enum intr_type flags, void **cookiep);
int intr_event_bind(struct intr_event *ie, int cpu);
+int intr_event_bind_irqonly(struct intr_event *ie, int cpu);
+int intr_event_bind_ithread(struct intr_event *ie, int cpu);
int intr_event_create(struct intr_event **event, void *source,
int flags, int irq, void (*pre_ithread)(void *),
void (*post_ithread)(void *), void (*post_filter)(void *),
@@ -173,9 +175,9 @@
void intr_event_execute_handlers(struct proc *p, struct intr_event *ie);
int intr_event_handle(struct intr_event *ie, struct trapframe *frame);
int intr_event_remove_handler(void *cookie);
-int intr_getaffinity(int irq, void *mask);
+int intr_getaffinity(int irq, int mode, void *mask);
void *intr_handler_source(void *cookie);
-int intr_setaffinity(int irq, void *mask);
+int intr_setaffinity(int irq, int mode, void *mask);
void _intr_drain(int irq); /* Linux compat only. */
int swi_add(struct intr_event **eventp, const char *name,
driver_intr_t handler, void *arg, int pri, enum intr_type flags,

File Metadata

Mime Type
text/plain
Expires
Thu, Oct 8, 2:50 AM (5 h, 42 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
40396863
Default Alt Text
D10435.id27945.diff (15 KB)

Event Timeline