Commit 95f9881f authored by arjanvandeven's avatar arjanvandeven

initial import


git-svn-id: https://irqbalance.googlecode.com/svn/trunk@2 46b42954-3823-0410-bd82-eb80b452c9b5
parent f34ec171
CFLAGS+=-g -Os -D_FORTIFY_SOURCE=2 -Wall -W `pkg-config --cflags glib-2.0`
all: irqbalance
LIBS=bitmap.o irqbalance.o cputree.o procinterrupts.o irqlist.o placement.o activate.o network.o powermode.o numa.o classify.o
irqbalance: .depend $(LIBS)
gcc -g -O2 -D_FORTIFY_SOURCE=2 -Wall `pkg-config --libs glib-2.0` $(LIBS) -o irqbalance
clean:
rm -f irqbalance *~ *.o .depend
# rule for building dependency lists, and writing them to a file
# named ".depend".
.depend:
rm -f .depend
gccmakedep -f- -- $(CFLAGS) -- *.c > .depend
/*
* Copyright (C) 2006, Intel Corporation
*
* This file is part of irqbalance
*
* This program file is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program in a file named COPYING; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA
*/
/*
* This file contains the code to communicate a selected distribution / mapping
* of interrupts to the kernel.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdint.h>
#include "irqbalance.h"
void activate_mapping(void)
{
struct interrupt *irq;
GList *iter;
iter = g_list_first(interrupts);
while (iter) {
irq = iter->data;
iter = g_list_next(iter);
if (!cpus_equal(irq->mask, irq->old_mask)) {
char buf[PATH_MAX];
FILE *file;
sprintf(buf, "/proc/irq/%i/smp_affinity", irq->number);
file = fopen(buf, "w");
if (!file)
continue;
cpumask_scnprintf(buf, PATH_MAX, irq->mask);
fprintf(file,"%s", buf);
fclose(file);
irq->old_mask = irq->mask;
}
}
}
/*
This file is taken from the Linux kernel and minimally adapted for use in userspace
*/
/*
* lib/bitmap.c
* Helper functions for bitmap.h.
*
* This source code is licensed under the GNU General Public License,
* Version 2. See the file COPYING for more details.
*/
#include <unistd.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include "bitmap.h"
#include "non-atomic.h"
/*
* bitmaps provide an array of bits, implemented using an an
* array of unsigned longs. The number of valid bits in a
* given bitmap does _not_ need to be an exact multiple of
* BITS_PER_LONG.
*
* The possible unused bits in the last, partially used word
* of a bitmap are 'don't care'. The implementation makes
* no particular effort to keep them zero. It ensures that
* their value will not affect the results of any operation.
* The bitmap operations that return Boolean (bitmap_empty,
* for example) or scalar (bitmap_weight, for example) results
* carefully filter out these unused bits from impacting their
* results.
*
* These operations actually hold to a slightly stronger rule:
* if you don't input any bitmaps to these ops that have some
* unused bits set, then they won't output any set unused bits
* in output bitmaps.
*
* The byte ordering of bitmaps is more natural on little
* endian architectures. See the big-endian headers
* include/asm-ppc64/bitops.h and include/asm-s390/bitops.h
* for the best explanations of this ordering.
*/
int __bitmap_empty(const unsigned long *bitmap, int bits)
{
int k, lim = bits/BITS_PER_LONG;
for (k = 0; k < lim; ++k)
if (bitmap[k])
return 0;
if (bits % BITS_PER_LONG)
if (bitmap[k] & BITMAP_LAST_WORD_MASK(bits))
return 0;
return 1;
}
int __bitmap_full(const unsigned long *bitmap, int bits)
{
int k, lim = bits/BITS_PER_LONG;
for (k = 0; k < lim; ++k)
if (~bitmap[k])
return 0;
if (bits % BITS_PER_LONG)
if (~bitmap[k] & BITMAP_LAST_WORD_MASK(bits))
return 0;
return 1;
}
int __bitmap_equal(const unsigned long *bitmap1,
const unsigned long *bitmap2, int bits)
{
int k, lim = bits/BITS_PER_LONG;
for (k = 0; k < lim; ++k)
if (bitmap1[k] != bitmap2[k])
return 0;
if (bits % BITS_PER_LONG)
if ((bitmap1[k] ^ bitmap2[k]) & BITMAP_LAST_WORD_MASK(bits))
return 0;
return 1;
}
void __bitmap_complement(unsigned long *dst, const unsigned long *src, int bits)
{
int k, lim = bits/BITS_PER_LONG;
for (k = 0; k < lim; ++k)
dst[k] = ~src[k];
if (bits % BITS_PER_LONG)
dst[k] = ~src[k] & BITMAP_LAST_WORD_MASK(bits);
}
/*
* __bitmap_shift_right - logical right shift of the bits in a bitmap
* @dst - destination bitmap
* @src - source bitmap
* @nbits - shift by this many bits
* @bits - bitmap size, in bits
*
* Shifting right (dividing) means moving bits in the MS -> LS bit
* direction. Zeros are fed into the vacated MS positions and the
* LS bits shifted off the bottom are lost.
*/
void __bitmap_shift_right(unsigned long *dst,
const unsigned long *src, int shift, int bits)
{
int k, lim = BITS_TO_LONGS(bits), left = bits % BITS_PER_LONG;
int off = shift/BITS_PER_LONG, rem = shift % BITS_PER_LONG;
unsigned long mask = (1UL << left) - 1;
for (k = 0; off + k < lim; ++k) {
unsigned long upper, lower;
/*
* If shift is not word aligned, take lower rem bits of
* word above and make them the top rem bits of result.
*/
if (!rem || off + k + 1 >= lim)
upper = 0;
else {
upper = src[off + k + 1];
if (off + k + 1 == lim - 1 && left)
upper &= mask;
}
lower = src[off + k];
if (left && off + k == lim - 1)
lower &= mask;
dst[k] = upper << (BITS_PER_LONG - rem) | lower >> rem;
if (left && k == lim - 1)
dst[k] &= mask;
}
if (off)
memset(&dst[lim - off], 0, off*sizeof(unsigned long));
}
/*
* __bitmap_shift_left - logical left shift of the bits in a bitmap
* @dst - destination bitmap
* @src - source bitmap
* @nbits - shift by this many bits
* @bits - bitmap size, in bits
*
* Shifting left (multiplying) means moving bits in the LS -> MS
* direction. Zeros are fed into the vacated LS bit positions
* and those MS bits shifted off the top are lost.
*/
void __bitmap_shift_left(unsigned long *dst,
const unsigned long *src, int shift, int bits)
{
int k, lim = BITS_TO_LONGS(bits), left = bits % BITS_PER_LONG;
int off = shift/BITS_PER_LONG, rem = shift % BITS_PER_LONG;
for (k = lim - off - 1; k >= 0; --k) {
unsigned long upper, lower;
/*
* If shift is not word aligned, take upper rem bits of
* word below and make them the bottom rem bits of result.
*/
if (rem && k > 0)
lower = src[k - 1];
else
lower = 0;
upper = src[k];
if (left && k == lim - 1)
upper &= (1UL << left) - 1;
dst[k + off] = lower >> (BITS_PER_LONG - rem) | upper << rem;
if (left && k + off == lim - 1)
dst[k + off] &= (1UL << left) - 1;
}
if (off)
memset(dst, 0, off*sizeof(unsigned long));
}
void __bitmap_and(unsigned long *dst, const unsigned long *bitmap1,
const unsigned long *bitmap2, int bits)
{
int k;
int nr = BITS_TO_LONGS(bits);
for (k = 0; k < nr; k++)
dst[k] = bitmap1[k] & bitmap2[k];
}
void __bitmap_or(unsigned long *dst, const unsigned long *bitmap1,
const unsigned long *bitmap2, int bits)
{
int k;
int nr = BITS_TO_LONGS(bits);
for (k = 0; k < nr; k++)
dst[k] = bitmap1[k] | bitmap2[k];
}
void __bitmap_xor(unsigned long *dst, const unsigned long *bitmap1,
const unsigned long *bitmap2, int bits)
{
int k;
int nr = BITS_TO_LONGS(bits);
for (k = 0; k < nr; k++)
dst[k] = bitmap1[k] ^ bitmap2[k];
}
void __bitmap_andnot(unsigned long *dst, const unsigned long *bitmap1,
const unsigned long *bitmap2, int bits)
{
int k;
int nr = BITS_TO_LONGS(bits);
for (k = 0; k < nr; k++)
dst[k] = bitmap1[k] & ~bitmap2[k];
}
int __bitmap_intersects(const unsigned long *bitmap1,
const unsigned long *bitmap2, int bits)
{
int k, lim = bits/BITS_PER_LONG;
for (k = 0; k < lim; ++k)
if (bitmap1[k] & bitmap2[k])
return 1;
if (bits % BITS_PER_LONG)
if ((bitmap1[k] & bitmap2[k]) & BITMAP_LAST_WORD_MASK(bits))
return 1;
return 0;
}
/*
* Bitmap printing & parsing functions: first version by Bill Irwin,
* second version by Paul Jackson, third by Joe Korty.
*/
#define CHUNKSZ 32
#define nbits_to_hold_value(val) fls(val)
#define unhex(c) (isdigit(c) ? (c - '0') : (toupper(c) - 'A' + 10))
#define BASEDEC 10 /* fancier cpuset lists input in decimal */
/**
* bitmap_scnprintf - convert bitmap to an ASCII hex string.
* @buf: byte buffer into which string is placed
* @buflen: reserved size of @buf, in bytes
* @maskp: pointer to bitmap to convert
* @nmaskbits: size of bitmap, in bits
*
* Exactly @nmaskbits bits are displayed. Hex digits are grouped into
* comma-separated sets of eight digits per set.
*/
int bitmap_scnprintf(char *buf, unsigned int buflen,
const unsigned long *maskp, int nmaskbits)
{
int i, word, bit, len = 0;
unsigned long val;
const char *sep = "";
int chunksz;
uint32_t chunkmask;
int first = 1;
chunksz = nmaskbits & (CHUNKSZ - 1);
if (chunksz == 0)
chunksz = CHUNKSZ;
i = ALIGN(nmaskbits, CHUNKSZ) - CHUNKSZ;
for (; i >= 0; i -= CHUNKSZ) {
chunkmask = ((1ULL << chunksz) - 1);
word = i / BITS_PER_LONG;
bit = i % BITS_PER_LONG;
val = (maskp[word] >> bit) & chunkmask;
if (val!=0 || !first) {
len += snprintf(buf+len, buflen-len, "%s%0*lx", sep,
(chunksz+3)/4, val);
chunksz = CHUNKSZ;
sep = ",";
first = 0;
}
}
return len;
}
/**
* __bitmap_parse - convert an ASCII hex string into a bitmap.
* @buf: pointer to buffer containing string.
* @buflen: buffer size in bytes. If string is smaller than this
* then it must be terminated with a \0.
* @is_user: location of buffer, 0 indicates kernel space
* @maskp: pointer to bitmap array that will contain result.
* @nmaskbits: size of bitmap, in bits.
*
* Commas group hex digits into chunks. Each chunk defines exactly 32
* bits of the resultant bitmask. No chunk may specify a value larger
* than 32 bits (%-EOVERFLOW), and if a chunk specifies a smaller value
* then leading 0-bits are prepended. %-EINVAL is returned for illegal
* characters and for grouping errors such as "1,,5", ",44", "," and "".
* Leading and trailing whitespace accepted, but not embedded whitespace.
*/
int __bitmap_parse(const char *buf, unsigned int buflen,
int is_user __attribute((unused)), unsigned long *maskp,
int nmaskbits)
{
int c, old_c, totaldigits, ndigits, nchunks, nbits;
uint32_t chunk;
bitmap_zero(maskp, nmaskbits);
nchunks = nbits = totaldigits = c = 0;
do {
chunk = ndigits = 0;
/* Get the next chunk of the bitmap */
while (buflen) {
old_c = c;
c = *buf++;
buflen--;
if (isspace(c))
continue;
/*
* If the last character was a space and the current
* character isn't '\0', we've got embedded whitespace.
* This is a no-no, so throw an error.
*/
if (totaldigits && c && isspace(old_c))
return 0;
/* A '\0' or a ',' signal the end of the chunk */
if (c == '\0' || c == ',')
break;
if (!isxdigit(c))
return -EINVAL;
/*
* Make sure there are at least 4 free bits in 'chunk'.
* If not, this hexdigit will overflow 'chunk', so
* throw an error.
*/
if (chunk & ~((1UL << (CHUNKSZ - 4)) - 1))
return -EOVERFLOW;
chunk = (chunk << 4) | unhex(c);
ndigits++; totaldigits++;
}
if (ndigits == 0)
return -EINVAL;
if (nchunks == 0 && chunk == 0)
continue;
__bitmap_shift_left(maskp, maskp, CHUNKSZ, nmaskbits);
*maskp |= chunk;
nchunks++;
nbits += (nchunks == 1) ? nbits_to_hold_value(chunk) : CHUNKSZ;
if (nbits > nmaskbits)
return -EOVERFLOW;
} while (buflen && c == ',');
return 0;
}
This diff is collapsed.
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include "irqbalance.h"
#include "types.h"
char *classes[] = {
"other",
"legacy",
"storage",
"timer",
"ethernet",
"fasteth",
0
};
int map_class_to_level[7] =
{ BALANCE_PACKAGE, BALANCE_CACHE, BALANCE_CACHE, BALANCE_NONE, BALANCE_CORE, BALANCE_CORE };
int class_counts[7];
/*
NOTE NOTE although that this file has a hard-coded list of modules, something missing is not
a big deal; the types are also set based on PCI class information when available.
*/
/*
Based on the original irqbalance code which is:
Copyright (C) 2003 Red Hat, Inc. All rights reserved.
Usage and distribution of this file are subject to the Gnu General Public License Version 2
that can be found at http://www.gnu.org/licenses/gpl.txt and the COPYING file as
distributed together with this file is included herein by reference.
Author: Arjan van de Ven <arjanv@redhat.com>
*/
static char *legacy_modules[] = {
"PS/2",
"serial",
"i8042",
"acpi",
"floppy",
"parport",
"keyboard",
"usb-ohci",
"usb-uhci",
"uhci_hcd",
"ohci_hcd",
"ehci_hcd",
"EMU10K1",
0
};
static char *timer_modules[] = {
"rtc",
"timer",
0
};
static char *storage_modules[] = {
"aic7xxx",
"aic79xx",
"ide",
"cciss",
"cpqarray",
"qla2",
"megaraid",
"fusion",
"libata",
"ohci1394",
"sym53c8xx",
0
};
static char *ethernet_modules[] = {
"eth",
"e100",
"eepro100",
"orinico_cs",
"wvlan_cs",
"3c5",
"HiSax",
0
};
int find_class(struct interrupt *irq, char *moduletext)
{
int guess = IRQ_OTHER;
int i;
if (moduletext == NULL)
return guess;
for (i=0; legacy_modules[i]; i++)
if (strstr(moduletext, legacy_modules[i]))
guess = IRQ_LEGACY;
for (i=0; storage_modules[i]; i++)
if (strstr(moduletext, storage_modules[i]))
guess = IRQ_SCSI;
for (i=0; timer_modules[i]; i++)
if (strstr(moduletext, timer_modules[i]))
guess = IRQ_TIMER;
for (i=0; ethernet_modules[i]; i++)
if (strstr(moduletext, ethernet_modules[i]))
guess = IRQ_ETH;
if (guess == IRQ_OTHER && irq->number==0)
guess = IRQ_TIMER;
if (guess > irq->class)
return guess;
return irq->class;
}
#ifndef __INCLUDE_GUARD_CONSTANTS_H
#define __INCLUDE_GUARD_CONSTANTS_H
/* interval between rebalance attempts in seconds */
#define SLEEP_INTERVAL 10
/* NUMA topology refresh intervals, in units of SLEEP_INTERVAL */
#define NUMA_REFRESH_INTERVAL 32
/* NIC interrupt refresh interval, in units of SLEEP_INTERVAL */
#define NIC_REFRESH_INTERVAL 32
/* minimum number of interrupts since boot for an interrupt to matter */
#define MIN_IRQ_COUNT 20
/* balancing tunings */
#define CROSS_PACKAGE_PENALTY 3000
#define NUMA_PENALTY 250
#define POWER_MODE_PACKAGE_THRESHOLD 10000
#define CLASS_VIOLATION_PENTALTY 6000
#define CORE_SPECIFIC_THRESHOLD 5000
/* power mode */
#define POWER_MODE_SOFTIRQ_THRESHOLD 20
#define POWER_MODE_HYSTERESIS 3
#endif
This diff is collapsed.
/*
* Copyright (C) 2006, Intel Corporation
*
* This file is part of irqbalance
*
* This program file is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program in a file named COPYING; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA
*/
/*
* This file contains the code to construct and manipulate a hierarchy of processors,
* cache domains and processor cores.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <dirent.h>
#include <glib.h>
#include "irqbalance.h"
GList *cpus;
GList *cache_domains;
GList *packages;
int package_count;
int cache_domain_count;
int core_count;
/* Users want to be able to keep interrupts away from some cpus; store these in a cpumask_t */
cpumask_t banned_cpus;
/*
it's convenient to have the complement of banned_cpus available so that
the AND operator can be used to mask out unwanted cpus
*/
static cpumask_t unbanned_cpus;
static void fill_packages(void)
{
GList *entry;
entry = g_list_first(cache_domains);
while (entry) {
struct package *package;
struct cache_domain *cache = NULL;
GList *entry2;
cache = entry->data;
entry2 = entry;
entry = g_list_next(entry);
if (cache->marker)
continue;
package = malloc(sizeof(struct package));
if (!package)
break;
memset(package, 0, sizeof(struct package));
package->mask = cache->package_mask;
package->number = cache->number;
while (entry2) {
struct cache_domain *cache2;
cache2 = entry2->data;
if (cpus_equal(cache->package_mask, cache2->package_mask)) {
cache2->marker = 1;
package->cache_domains = g_list_append(package->cache_domains, cache2);
if (package->number > cache2->number)
package->number = cache2->number;
}
entry2 = g_list_next(entry2);
}
packages = g_list_append(packages, package);
package_count++;
}
}
static void fill_cache_domain(void)
{
GList *entry;
entry = g_list_first(cpus);
while (entry) {
struct cache_domain *cache = NULL;
struct cpu_core *cpu;
GList *entry2;
cpu = entry->data;
entry2 = entry;
entry = g_list_next(entry);
if (cpu->marker)
continue;
cache = malloc(sizeof(struct cache_domain));
if (!cache)
break;
memset(cache, 0, sizeof(struct cache_domain));
cache->mask = cpu->cache_mask;
cache->package_mask = cpu->package_mask;
cache->number = cpu->number;
cache_domains = g_list_append(cache_domains, cache);
cache_domain_count++;
while (entry2) {
struct cpu_core *cpu2;
cpu2 = entry2->data;
if (cpus_equal(cpu->cache_mask, cpu2->cache_mask) &&
cpus_equal(cpu->package_mask, cpu2->package_mask)) {
cpu2->marker = 1;
cache->cpu_cores = g_list_append(cache->cpu_cores, cpu2);
if (cpu2->number < cache->number)
cache->number = cpu2->number;
}
entry2 = g_list_next(entry2);
}
}
}
static void do_one_cpu(char *path)
{
struct cpu_core *cpu;
FILE *file;
char new_path[PATH_MAX];
/* skip offline cpus */
snprintf(new_path, PATH_MAX, "%s/online", path);
file = fopen(new_path, "r");
if (file) {
char line[4096];
line[4095]=0;
if (fgets(line, 4095, file)==NULL)
line[0]='1';
fclose(file);
if (line[0]=='0')
return;
}
cpu = malloc(sizeof(struct cpu_core));
if (!cpu)
return;
memset(cpu, 0, sizeof(struct cpu_core));
cpu->number = strtoul(&path[27], NULL, 10);
cpu_set(cpu->number, cpu->mask);
/* if the cpu is on the banned list, just don't add it */
if (cpus_intersects(cpu->mask, banned_cpus)) {