initial import
git-svn-id: https://irqbalance.googlecode.com/svn/trunk@2 46b42954-3823-0410-bd82-eb80b452c9b5
This commit is contained in:
parent
f34ec171d9
commit
95f9881ff8
|
@ -0,0 +1,17 @@
|
||||||
|
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
|
|
@ -0,0 +1,57 @@
|
||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,366 @@
|
||||||
|
/*
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
|
@ -0,0 +1,356 @@
|
||||||
|
#ifndef __LINUX_BITMAP_H
|
||||||
|
#define __LINUX_BITMAP_H
|
||||||
|
|
||||||
|
#ifndef __ASSEMBLY__
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
|
||||||
|
#define BITS_PER_LONG ((int)sizeof(unsigned long)*8)
|
||||||
|
|
||||||
|
#define BITS_TO_LONGS(bits) \
|
||||||
|
(((bits)+BITS_PER_LONG-1)/BITS_PER_LONG)
|
||||||
|
#define DECLARE_BITMAP(name,bits) \
|
||||||
|
unsigned long name[BITS_TO_LONGS(bits)]
|
||||||
|
#define ALIGN(x,a) (((x)+(a)-1UL)&~((a)-1UL))
|
||||||
|
|
||||||
|
|
||||||
|
#include "non-atomic.h"
|
||||||
|
|
||||||
|
static inline unsigned int hweight32(unsigned int w)
|
||||||
|
{
|
||||||
|
unsigned int res = w - ((w >> 1) & 0x55555555);
|
||||||
|
res = (res & 0x33333333) + ((res >> 2) & 0x33333333);
|
||||||
|
res = (res + (res >> 4)) & 0x0F0F0F0F;
|
||||||
|
res = res + (res >> 8);
|
||||||
|
return (res + (res >> 16)) & 0x000000FF;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline unsigned long hweight64(uint64_t w)
|
||||||
|
{
|
||||||
|
if (BITS_PER_LONG == 32)
|
||||||
|
return hweight32((unsigned int)(w >> 32)) + hweight32((unsigned int)w);
|
||||||
|
|
||||||
|
w -= (w >> 1) & 0x5555555555555555ull;
|
||||||
|
w = (w & 0x3333333333333333ull) + ((w >> 2) & 0x3333333333333333ull);
|
||||||
|
w = (w + (w >> 4)) & 0x0f0f0f0f0f0f0f0full;
|
||||||
|
return (w * 0x0101010101010101ull) >> 56;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static inline int fls(int x)
|
||||||
|
{
|
||||||
|
int r = 32;
|
||||||
|
|
||||||
|
if (!x)
|
||||||
|
return 0;
|
||||||
|
if (!(x & 0xffff0000u)) {
|
||||||
|
x <<= 16;
|
||||||
|
r -= 16;
|
||||||
|
}
|
||||||
|
if (!(x & 0xff000000u)) {
|
||||||
|
x <<= 8;
|
||||||
|
r -= 8;
|
||||||
|
}
|
||||||
|
if (!(x & 0xf0000000u)) {
|
||||||
|
x <<= 4;
|
||||||
|
r -= 4;
|
||||||
|
}
|
||||||
|
if (!(x & 0xc0000000u)) {
|
||||||
|
x <<= 2;
|
||||||
|
r -= 2;
|
||||||
|
}
|
||||||
|
if (!(x & 0x80000000u)) {
|
||||||
|
x <<= 1;
|
||||||
|
r -= 1;
|
||||||
|
}
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline unsigned long hweight_long(unsigned long w)
|
||||||
|
{
|
||||||
|
return sizeof(w) == 4 ? hweight32(w) : hweight64(w);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define min(x,y) ({ \
|
||||||
|
typeof(x) _x = (x); \
|
||||||
|
typeof(y) _y = (y); \
|
||||||
|
(void) (&_x == &_y); \
|
||||||
|
_x < _y ? _x : _y; })
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* bitmaps provide bit arrays that consume one or more unsigned
|
||||||
|
* longs. The bitmap interface and available operations are listed
|
||||||
|
* here, in bitmap.h
|
||||||
|
*
|
||||||
|
* Function implementations generic to all architectures are in
|
||||||
|
* lib/bitmap.c. Functions implementations that are architecture
|
||||||
|
* specific are in various include/asm-<arch>/bitops.h headers
|
||||||
|
* and other arch/<arch> specific files.
|
||||||
|
*
|
||||||
|
* See lib/bitmap.c for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The available bitmap operations and their rough meaning in the
|
||||||
|
* case that the bitmap is a single unsigned long are thus:
|
||||||
|
*
|
||||||
|
* Note that nbits should be always a compile time evaluable constant.
|
||||||
|
* Otherwise many inlines will generate horrible code.
|
||||||
|
*
|
||||||
|
* bitmap_zero(dst, nbits) *dst = 0UL
|
||||||
|
* bitmap_fill(dst, nbits) *dst = ~0UL
|
||||||
|
* bitmap_copy(dst, src, nbits) *dst = *src
|
||||||
|
* bitmap_and(dst, src1, src2, nbits) *dst = *src1 & *src2
|
||||||
|
* bitmap_or(dst, src1, src2, nbits) *dst = *src1 | *src2
|
||||||
|
* bitmap_xor(dst, src1, src2, nbits) *dst = *src1 ^ *src2
|
||||||
|
* bitmap_andnot(dst, src1, src2, nbits) *dst = *src1 & ~(*src2)
|
||||||
|
* bitmap_complement(dst, src, nbits) *dst = ~(*src)
|
||||||
|
* bitmap_equal(src1, src2, nbits) Are *src1 and *src2 equal?
|
||||||
|
* bitmap_intersects(src1, src2, nbits) Do *src1 and *src2 overlap?
|
||||||
|
* bitmap_subset(src1, src2, nbits) Is *src1 a subset of *src2?
|
||||||
|
* bitmap_empty(src, nbits) Are all bits zero in *src?
|
||||||
|
* bitmap_full(src, nbits) Are all bits set in *src?
|
||||||
|
* bitmap_weight(src, nbits) Hamming Weight: number set bits
|
||||||
|
* bitmap_shift_right(dst, src, n, nbits) *dst = *src >> n
|
||||||
|
* bitmap_shift_left(dst, src, n, nbits) *dst = *src << n
|
||||||
|
* bitmap_remap(dst, src, old, new, nbits) *dst = map(old, new)(src)
|
||||||
|
* bitmap_bitremap(oldbit, old, new, nbits) newbit = map(old, new)(oldbit)
|
||||||
|
* bitmap_scnprintf(buf, len, src, nbits) Print bitmap src to buf
|
||||||
|
* bitmap_parse(buf, buflen, dst, nbits) Parse bitmap dst from kernel buf
|
||||||
|
* bitmap_parse_user(ubuf, ulen, dst, nbits) Parse bitmap dst from user buf
|
||||||
|
* bitmap_scnlistprintf(buf, len, src, nbits) Print bitmap src as list to buf
|
||||||
|
* bitmap_parselist(buf, dst, nbits) Parse bitmap dst from list
|
||||||
|
* bitmap_find_free_region(bitmap, bits, order) Find and allocate bit region
|
||||||
|
* bitmap_release_region(bitmap, pos, order) Free specified bit region
|
||||||
|
* bitmap_allocate_region(bitmap, pos, order) Allocate specified bit region
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Also the following operations in asm/bitops.h apply to bitmaps.
|
||||||
|
*
|
||||||
|
* set_bit(bit, addr) *addr |= bit
|
||||||
|
* clear_bit(bit, addr) *addr &= ~bit
|
||||||
|
* change_bit(bit, addr) *addr ^= bit
|
||||||
|
* test_bit(bit, addr) Is bit set in *addr?
|
||||||
|
* test_and_set_bit(bit, addr) Set bit and return old value
|
||||||
|
* test_and_clear_bit(bit, addr) Clear bit and return old value
|
||||||
|
* test_and_change_bit(bit, addr) Change bit and return old value
|
||||||
|
* find_first_zero_bit(addr, nbits) Position first zero bit in *addr
|
||||||
|
* find_first_bit(addr, nbits) Position first set bit in *addr
|
||||||
|
* find_next_zero_bit(addr, nbits, bit) Position next zero bit in *addr >= bit
|
||||||
|
* find_next_bit(addr, nbits, bit) Position next set bit in *addr >= bit
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The DECLARE_BITMAP(name,bits) macro, in linux/types.h, can be used
|
||||||
|
* to declare an array named 'name' of just enough unsigned longs to
|
||||||
|
* contain all bit positions from 0 to 'bits' - 1.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* lib/bitmap.c provides these functions:
|
||||||
|
*/
|
||||||
|
|
||||||
|
extern int __bitmap_empty(const unsigned long *bitmap, int bits);
|
||||||
|
extern int __bitmap_full(const unsigned long *bitmap, int bits);
|
||||||
|
extern int __bitmap_equal(const unsigned long *bitmap1,
|
||||||
|
const unsigned long *bitmap2, int bits);
|
||||||
|
extern void __bitmap_complement(unsigned long *dst, const unsigned long *src,
|
||||||
|
int bits);
|
||||||
|
extern void __bitmap_shift_right(unsigned long *dst,
|
||||||
|
const unsigned long *src, int shift, int bits);
|
||||||
|
extern void __bitmap_shift_left(unsigned long *dst,
|
||||||
|
const unsigned long *src, int shift, int bits);
|
||||||
|
extern void __bitmap_and(unsigned long *dst, const unsigned long *bitmap1,
|
||||||
|
const unsigned long *bitmap2, int bits);
|
||||||
|
extern void __bitmap_or(unsigned long *dst, const unsigned long *bitmap1,
|
||||||
|
const unsigned long *bitmap2, int bits);
|
||||||
|
extern void __bitmap_xor(unsigned long *dst, const unsigned long *bitmap1,
|
||||||
|
const unsigned long *bitmap2, int bits);
|
||||||
|
extern void __bitmap_andnot(unsigned long *dst, const unsigned long *bitmap1,
|
||||||
|
const unsigned long *bitmap2, int bits);
|
||||||
|
extern int __bitmap_intersects(const unsigned long *bitmap1,
|
||||||
|
const unsigned long *bitmap2, int bits);
|
||||||
|
extern int __bitmap_subset(const unsigned long *bitmap1,
|
||||||
|
const unsigned long *bitmap2, int bits);
|
||||||
|
extern int __bitmap_weight(const unsigned long *bitmap, int bits);
|
||||||
|
|
||||||
|
extern int bitmap_scnprintf(char *buf, unsigned int len,
|
||||||
|
const unsigned long *src, int nbits);
|
||||||
|
extern int __bitmap_parse(const char *buf, unsigned int buflen, int is_user,
|
||||||
|
unsigned long *dst, int nbits);
|
||||||
|
extern int bitmap_scnlistprintf(char *buf, unsigned int len,
|
||||||
|
const unsigned long *src, int nbits);
|
||||||
|
extern int bitmap_parselist(const char *buf, unsigned long *maskp,
|
||||||
|
int nmaskbits);
|
||||||
|
extern void bitmap_remap(unsigned long *dst, const unsigned long *src,
|
||||||
|
const unsigned long *old, const unsigned long *new, int bits);
|
||||||
|
extern int bitmap_bitremap(int oldbit,
|
||||||
|
const unsigned long *old, const unsigned long *new, int bits);
|
||||||
|
extern int bitmap_find_free_region(unsigned long *bitmap, int bits, int order);
|
||||||
|
extern void bitmap_release_region(unsigned long *bitmap, int pos, int order);
|
||||||
|
extern int bitmap_allocate_region(unsigned long *bitmap, int pos, int order);
|
||||||
|
|
||||||
|
#define BITMAP_LAST_WORD_MASK(nbits) \
|
||||||
|
( \
|
||||||
|
((nbits) % BITS_PER_LONG) ? \
|
||||||
|
(1UL<<((nbits) % BITS_PER_LONG))-1 : ~0UL \
|
||||||
|
)
|
||||||
|
|
||||||
|
static inline void bitmap_zero(unsigned long *dst, int nbits)
|
||||||
|
{
|
||||||
|
if (nbits <= BITS_PER_LONG)
|
||||||
|
*dst = 0UL;
|
||||||
|
else {
|
||||||
|
int len = BITS_TO_LONGS(nbits) * sizeof(unsigned long);
|
||||||
|
memset(dst, 0, len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void bitmap_fill(unsigned long *dst, int nbits)
|
||||||
|
{
|
||||||
|
size_t nlongs = BITS_TO_LONGS(nbits);
|
||||||
|
if (nlongs > 1) {
|
||||||
|
int len = (nlongs - 1) * sizeof(unsigned long);
|
||||||
|
memset(dst, 0xff, len);
|
||||||
|
}
|
||||||
|
dst[nlongs - 1] = BITMAP_LAST_WORD_MASK(nbits);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void bitmap_copy(unsigned long *dst, const unsigned long *src,
|
||||||
|
int nbits)
|
||||||
|
{
|
||||||
|
if (nbits <= BITS_PER_LONG)
|
||||||
|
*dst = *src;
|
||||||
|
else {
|
||||||
|
int len = BITS_TO_LONGS(nbits) * sizeof(unsigned long);
|
||||||
|
memcpy(dst, src, len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void bitmap_and(unsigned long *dst, const unsigned long *src1,
|
||||||
|
const unsigned long *src2, int nbits)
|
||||||
|
{
|
||||||
|
if (nbits <= BITS_PER_LONG)
|
||||||
|
*dst = *src1 & *src2;
|
||||||
|
else
|
||||||
|
__bitmap_and(dst, src1, src2, nbits);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void bitmap_or(unsigned long *dst, const unsigned long *src1,
|
||||||
|
const unsigned long *src2, int nbits)
|
||||||
|
{
|
||||||
|
if (nbits <= BITS_PER_LONG)
|
||||||
|
*dst = *src1 | *src2;
|
||||||
|
else
|
||||||
|
__bitmap_or(dst, src1, src2, nbits);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void bitmap_xor(unsigned long *dst, const unsigned long *src1,
|
||||||
|
const unsigned long *src2, int nbits)
|
||||||
|
{
|
||||||
|
if (nbits <= BITS_PER_LONG)
|
||||||
|
*dst = *src1 ^ *src2;
|
||||||
|
else
|
||||||
|
__bitmap_xor(dst, src1, src2, nbits);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void bitmap_andnot(unsigned long *dst, const unsigned long *src1,
|
||||||
|
const unsigned long *src2, int nbits)
|
||||||
|
{
|
||||||
|
if (nbits <= BITS_PER_LONG)
|
||||||
|
*dst = *src1 & ~(*src2);
|
||||||
|
else
|
||||||
|
__bitmap_andnot(dst, src1, src2, nbits);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void bitmap_complement(unsigned long *dst, const unsigned long *src,
|
||||||
|
int nbits)
|
||||||
|
{
|
||||||
|
if (nbits <= BITS_PER_LONG)
|
||||||
|
*dst = ~(*src) & BITMAP_LAST_WORD_MASK(nbits);
|
||||||
|
else
|
||||||
|
__bitmap_complement(dst, src, nbits);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int bitmap_equal(const unsigned long *src1,
|
||||||
|
const unsigned long *src2, int nbits)
|
||||||
|
{
|
||||||
|
if (nbits <= BITS_PER_LONG)
|
||||||
|
return ! ((*src1 ^ *src2) & BITMAP_LAST_WORD_MASK(nbits));
|
||||||
|
else
|
||||||
|
return __bitmap_equal(src1, src2, nbits);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int bitmap_intersects(const unsigned long *src1,
|
||||||
|
const unsigned long *src2, int nbits)
|
||||||
|
{
|
||||||
|
if (nbits <= BITS_PER_LONG)
|
||||||
|
return ((*src1 & *src2) & BITMAP_LAST_WORD_MASK(nbits)) != 0;
|
||||||
|
else
|
||||||
|
return __bitmap_intersects(src1, src2, nbits);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int bitmap_subset(const unsigned long *src1,
|
||||||
|
const unsigned long *src2, int nbits)
|
||||||
|
{
|
||||||
|
if (nbits <= BITS_PER_LONG)
|
||||||
|
return ! ((*src1 & ~(*src2)) & BITMAP_LAST_WORD_MASK(nbits));
|
||||||
|
else
|
||||||
|
return __bitmap_subset(src1, src2, nbits);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int bitmap_empty(const unsigned long *src, int nbits)
|
||||||
|
{
|
||||||
|
if (nbits <= BITS_PER_LONG)
|
||||||
|
return ! (*src & BITMAP_LAST_WORD_MASK(nbits));
|
||||||
|
else
|
||||||
|
return __bitmap_empty(src, nbits);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int bitmap_full(const unsigned long *src, int nbits)
|
||||||
|
{
|
||||||
|
if (nbits <= BITS_PER_LONG)
|
||||||
|
return ! (~(*src) & BITMAP_LAST_WORD_MASK(nbits));
|
||||||
|
else
|
||||||
|
return __bitmap_full(src, nbits);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int bitmap_weight(const unsigned long *src, int nbits)
|
||||||
|
{
|
||||||
|
if (nbits <= BITS_PER_LONG)
|
||||||
|
return hweight_long(*src & BITMAP_LAST_WORD_MASK(nbits));
|
||||||
|
return __bitmap_weight(src, nbits);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void bitmap_shift_right(unsigned long *dst,
|
||||||
|
const unsigned long *src, int n, int nbits)
|
||||||
|
{
|
||||||
|
if (nbits <= BITS_PER_LONG)
|
||||||
|
*dst = *src >> n;
|
||||||
|
else
|
||||||
|
__bitmap_shift_right(dst, src, n, nbits);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void bitmap_shift_left(unsigned long *dst,
|
||||||
|
const unsigned long *src, int n, int nbits)
|
||||||
|
{
|
||||||
|
if (nbits <= BITS_PER_LONG)
|
||||||
|
*dst = (*src << n) & BITMAP_LAST_WORD_MASK(nbits);
|
||||||
|
else
|
||||||
|
__bitmap_shift_left(dst, src, n, nbits);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int bitmap_parse(const char *buf, unsigned int buflen,
|
||||||
|
unsigned long *maskp, int nmaskbits)
|
||||||
|
{
|
||||||
|
return __bitmap_parse(buf, buflen, 0, maskp, nmaskbits);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* __ASSEMBLY__ */
|
||||||
|
|
||||||
|
#endif /* __LINUX_BITMAP_H */
|
|
@ -0,0 +1,126 @@
|
||||||
|
#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;
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
#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
|
|
@ -0,0 +1,400 @@
|
||||||
|
#ifndef __LINUX_CPUMASK_H
|
||||||
|
#define __LINUX_CPUMASK_H
|
||||||
|
|
||||||
|
#define NR_CPUS 256
|
||||||
|
/*
|
||||||
|
* Cpumasks provide a bitmap suitable for representing the
|
||||||
|
* set of CPU's in a system, one bit position per CPU number.
|
||||||
|
*
|
||||||
|
* See detailed comments in the file linux/bitmap.h describing the
|
||||||
|
* data type on which these cpumasks are based.
|
||||||
|
*
|
||||||
|
* For details of cpumask_scnprintf() and cpumask_parse_user(),
|
||||||
|
* see bitmap_scnprintf() and bitmap_parse_user() in lib/bitmap.c.
|
||||||
|
* For details of cpulist_scnprintf() and cpulist_parse(), see
|
||||||
|
* bitmap_scnlistprintf() and bitmap_parselist(), also in bitmap.c.
|
||||||
|
* For details of cpu_remap(), see bitmap_bitremap in lib/bitmap.c
|
||||||
|
* For details of cpus_remap(), see bitmap_remap in lib/bitmap.c.
|
||||||
|
*
|
||||||
|
* The available cpumask operations are:
|
||||||
|
*
|
||||||
|
* void cpu_set(cpu, mask) turn on bit 'cpu' in mask
|
||||||
|
* void cpu_clear(cpu, mask) turn off bit 'cpu' in mask
|
||||||
|
* void cpus_setall(mask) set all bits
|
||||||
|
* void cpus_clear(mask) clear all bits
|
||||||
|
* int cpu_isset(cpu, mask) true iff bit 'cpu' set in mask
|
||||||
|
* int cpu_test_and_set(cpu, mask) test and set bit 'cpu' in mask
|
||||||
|
*
|
||||||
|
* void cpus_and(dst, src1, src2) dst = src1 & src2 [intersection]
|
||||||
|
* void cpus_or(dst, src1, src2) dst = src1 | src2 [union]
|
||||||
|
* void cpus_xor(dst, src1, src2) dst = src1 ^ src2
|
||||||
|
* void cpus_andnot(dst, src1, src2) dst = src1 & ~src2
|
||||||
|
* void cpus_complement(dst, src) dst = ~src
|
||||||
|
*
|
||||||
|
* int cpus_equal(mask1, mask2) Does mask1 == mask2?
|
||||||
|
* int cpus_intersects(mask1, mask2) Do mask1 and mask2 intersect?
|
||||||
|
* int cpus_subset(mask1, mask2) Is mask1 a subset of mask2?
|
||||||
|
* int cpus_empty(mask) Is mask empty (no bits sets)?
|
||||||
|
* int cpus_full(mask) Is mask full (all bits sets)?
|
||||||
|
* int cpus_weight(mask) Hamming weigh - number of set bits
|
||||||
|
*
|
||||||
|
* void cpus_shift_right(dst, src, n) Shift right
|
||||||
|
* void cpus_shift_left(dst, src, n) Shift left
|
||||||
|
*
|
||||||
|
* int first_cpu(mask) Number lowest set bit, or NR_CPUS
|
||||||
|
* int next_cpu(cpu, mask) Next cpu past 'cpu', or NR_CPUS
|
||||||
|
*
|
||||||
|
* cpumask_t cpumask_of_cpu(cpu) Return cpumask with bit 'cpu' set
|
||||||
|
* CPU_MASK_ALL Initializer - all bits set
|
||||||
|
* CPU_MASK_NONE Initializer - no bits set
|
||||||
|
* unsigned long *cpus_addr(mask) Array of unsigned long's in mask
|
||||||
|
*
|
||||||
|
* int cpumask_scnprintf(buf, len, mask) Format cpumask for printing
|
||||||
|
* int cpumask_parse_user(ubuf, ulen, mask) Parse ascii string as cpumask
|
||||||
|
* int cpulist_scnprintf(buf, len, mask) Format cpumask as list for printing
|
||||||
|
* int cpulist_parse(buf, map) Parse ascii string as cpulist
|
||||||
|
* int cpu_remap(oldbit, old, new) newbit = map(old, new)(oldbit)
|
||||||
|
* int cpus_remap(dst, src, old, new) *dst = map(old, new)(src)
|
||||||
|
*
|
||||||
|
* for_each_cpu_mask(cpu, mask) for-loop cpu over mask
|
||||||
|
*
|
||||||
|
* int num_online_cpus() Number of online CPUs
|
||||||
|
* int num_possible_cpus() Number of all possible CPUs
|
||||||
|
* int num_present_cpus() Number of present CPUs
|
||||||
|
*
|
||||||
|
* int cpu_online(cpu) Is some cpu online?
|
||||||
|
* int cpu_possible(cpu) Is some cpu possible?
|
||||||
|
* int cpu_present(cpu) Is some cpu present (can schedule)?
|
||||||
|
*
|
||||||
|
* int any_online_cpu(mask) First online cpu in mask
|
||||||
|
*
|
||||||
|
* for_each_possible_cpu(cpu) for-loop cpu over cpu_possible_map
|
||||||
|
* for_each_online_cpu(cpu) for-loop cpu over cpu_online_map
|
||||||
|
* for_each_present_cpu(cpu) for-loop cpu over cpu_present_map
|
||||||
|
*
|
||||||
|
* Subtlety:
|
||||||
|
* 1) The 'type-checked' form of cpu_isset() causes gcc (3.3.2, anyway)
|
||||||
|
* to generate slightly worse code. Note for example the additional
|
||||||
|
* 40 lines of assembly code compiling the "for each possible cpu"
|
||||||
|
* loops buried in the disk_stat_read() macros calls when compiling
|
||||||
|
* drivers/block/genhd.c (arch i386, CONFIG_SMP=y). So use a simple
|
||||||
|
* one-line #define for cpu_isset(), instead of wrapping an inline
|
||||||
|
* inside a macro, the way we do the other calls.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "bitmap.h"
|
||||||
|
|
||||||
|
typedef struct { DECLARE_BITMAP(bits, NR_CPUS); } cpumask_t;
|
||||||
|
extern cpumask_t _unused_cpumask_arg_;
|
||||||
|
|
||||||
|
#define cpu_set(cpu, dst) __cpu_set((cpu), &(dst))
|
||||||
|
static inline void __cpu_set(int cpu, volatile cpumask_t *dstp)
|
||||||
|
{
|
||||||
|
set_bit(cpu, dstp->bits);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define cpu_clear(cpu, dst) __cpu_clear((cpu), &(dst))
|
||||||
|
static inline void __cpu_clear(int cpu, volatile cpumask_t *dstp)
|
||||||
|
{
|
||||||
|
clear_bit(cpu, dstp->bits);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define cpus_setall(dst) __cpus_setall(&(dst), NR_CPUS)
|
||||||
|
static inline void __cpus_setall(cpumask_t *dstp, int nbits)
|
||||||
|
{
|
||||||
|
bitmap_fill(dstp->bits, nbits);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define cpus_clear(dst) __cpus_clear(&(dst), NR_CPUS)
|
||||||
|
static inline void __cpus_clear(cpumask_t *dstp, int nbits)
|
||||||
|
{
|
||||||
|
bitmap_zero(dstp->bits, nbits);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* No static inline type checking - see Subtlety (1) above. */
|
||||||
|
#define cpu_isset(cpu, cpumask) test_bit((cpu), (cpumask).bits)
|
||||||
|
|
||||||
|
#define cpus_and(dst, src1, src2) __cpus_and(&(dst), &(src1), &(src2), NR_CPUS)
|
||||||
|
static inline void __cpus_and(cpumask_t *dstp, const cpumask_t *src1p,
|
||||||
|
const cpumask_t *src2p, int nbits)
|
||||||
|
{
|
||||||
|
bitmap_and(dstp->bits, src1p->bits, src2p->bits, nbits);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define cpus_or(dst, src1, src2) __cpus_or(&(dst), &(src1), &(src2), NR_CPUS)
|
||||||
|
static inline void __cpus_or(cpumask_t *dstp, const cpumask_t *src1p,
|
||||||
|
const cpumask_t *src2p, int nbits)
|
||||||
|
{
|
||||||
|
bitmap_or(dstp->bits, src1p->bits, src2p->bits, nbits);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define cpus_xor(dst, src1, src2) __cpus_xor(&(dst), &(src1), &(src2), NR_CPUS)
|
||||||
|
static inline void __cpus_xor(cpumask_t *dstp, const cpumask_t *src1p,
|
||||||
|
const cpumask_t *src2p, int nbits)
|
||||||
|
{
|
||||||
|
bitmap_xor(dstp->bits, src1p->bits, src2p->bits, nbits);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define cpus_andnot(dst, src1, src2) \
|
||||||
|
__cpus_andnot(&(dst), &(src1), &(src2), NR_CPUS)
|
||||||
|
static inline void __cpus_andnot(cpumask_t *dstp, const cpumask_t *src1p,
|
||||||
|
const cpumask_t *src2p, int nbits)
|
||||||
|
{
|
||||||
|
bitmap_andnot(dstp->bits, src1p->bits, src2p->bits, nbits);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define cpus_complement(dst, src) __cpus_complement(&(dst), &(src), NR_CPUS)
|
||||||
|
static inline void __cpus_complement(cpumask_t *dstp,
|
||||||
|
const cpumask_t *srcp, int nbits)
|
||||||
|
{
|
||||||
|
bitmap_complement(dstp->bits, srcp->bits, nbits);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define cpus_equal(src1, src2) __cpus_equal(&(src1), &(src2), NR_CPUS)
|
||||||
|
static inline int __cpus_equal(const cpumask_t *src1p,
|
||||||
|
const cpumask_t *src2p, int nbits)
|
||||||
|
{
|
||||||
|
return bitmap_equal(src1p->bits, src2p->bits, nbits);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define cpus_intersects(src1, src2) __cpus_intersects(&(src1), &(src2), NR_CPUS)
|
||||||
|
static inline int __cpus_intersects(const cpumask_t *src1p,
|
||||||
|
const cpumask_t *src2p, int nbits)
|
||||||
|
{
|
||||||
|
return bitmap_intersects(src1p->bits, src2p->bits, nbits);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define cpus_subset(src1, src2) __cpus_subset(&(src1), &(src2), NR_CPUS)
|
||||||
|
static inline int __cpus_subset(const cpumask_t *src1p,
|
||||||
|
const cpumask_t *src2p, int nbits)
|
||||||
|
{
|
||||||
|
return bitmap_subset(src1p->bits, src2p->bits, nbits);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define cpus_empty(src) __cpus_empty(&(src), NR_CPUS)
|
||||||
|
static inline int __cpus_empty(const cpumask_t *srcp, int nbits)
|
||||||
|
{
|
||||||
|
return bitmap_empty(srcp->bits, nbits);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define cpus_full(cpumask) __cpus_full(&(cpumask), NR_CPUS)
|
||||||
|
static inline int __cpus_full(const cpumask_t *srcp, int nbits)
|
||||||
|
{
|
||||||
|
return bitmap_full(srcp->bits, nbits);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define cpus_weight(cpumask) __cpus_weight(&(cpumask), NR_CPUS)
|
||||||
|
static inline int __cpus_weight(const cpumask_t *srcp, int nbits)
|
||||||
|
{
|
||||||
|
return bitmap_weight(srcp->bits, nbits);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define cpus_shift_right(dst, src, n) \
|
||||||
|
__cpus_shift_right(&(dst), &(src), (n), NR_CPUS)
|
||||||
|
static inline void __cpus_shift_right(cpumask_t *dstp,
|
||||||
|
const cpumask_t *srcp, int n, int nbits)
|
||||||
|
{
|
||||||
|
bitmap_shift_right(dstp->bits, srcp->bits, n, nbits);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define cpus_shift_left(dst, src, n) \
|
||||||
|
__cpus_shift_left(&(dst), &(src), (n), NR_CPUS)
|
||||||
|
static inline void __cpus_shift_left(cpumask_t *dstp,
|
||||||
|
const cpumask_t *srcp, int n, int nbits)
|
||||||
|
{
|
||||||
|
bitmap_shift_left(dstp->bits, srcp->bits, n, nbits);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int __first_cpu(const cpumask_t *srcp)
|
||||||
|
{
|
||||||
|
return ffs(*srcp->bits)-1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define first_cpu(src) __first_cpu(&(src))
|
||||||
|
int __next_cpu(int n, const cpumask_t *srcp);
|
||||||
|
#define next_cpu(n, src) __next_cpu((n), &(src))
|
||||||
|
|
||||||
|
#define cpumask_of_cpu(cpu) \
|
||||||
|
({ \
|
||||||
|
typeof(_unused_cpumask_arg_) m; \
|
||||||
|
if (sizeof(m) == sizeof(unsigned long)) { \
|
||||||
|
m.bits[0] = 1UL<<(cpu); \
|
||||||
|
} else { \
|
||||||
|
cpus_clear(m); \
|
||||||
|
cpu_set((cpu), m); \
|
||||||
|
} \
|
||||||
|
m; \
|
||||||
|
})
|
||||||
|
|
||||||
|
#define CPU_MASK_LAST_WORD BITMAP_LAST_WORD_MASK(NR_CPUS)
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
|
||||||
|
#define CPU_MASK_ALL \
|
||||||
|
(cpumask_t) { { \
|
||||||
|
[BITS_TO_LONGS(NR_CPUS)-1] = CPU_MASK_LAST_WORD \
|
||||||
|
} }
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#define CPU_MASK_ALL \
|
||||||
|
(cpumask_t) { { \
|
||||||
|
[0 ... BITS_TO_LONGS(NR_CPUS)-2] = ~0UL, \
|
||||||
|
[BITS_TO_LONGS(NR_CPUS)-1] = CPU_MASK_LAST_WORD \
|
||||||
|
} }
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define CPU_MASK_NONE \
|
||||||
|
(cpumask_t) { { \
|
||||||
|
[0 ... BITS_TO_LONGS(NR_CPUS)-1] = 0UL \
|
||||||
|
} }
|
||||||
|
|
||||||
|
#define CPU_MASK_CPU0 \
|
||||||
|
(cpumask_t) { { \
|
||||||
|
[0] = 1UL \
|
||||||
|
} }
|
||||||
|
|
||||||
|
#define cpus_addr(src) ((src).bits)
|
||||||
|
|
||||||
|
#define cpumask_scnprintf(buf, len, src) \
|
||||||
|
__cpumask_scnprintf((buf), (len), &(src), NR_CPUS)
|
||||||
|
static inline int __cpumask_scnprintf(char *buf, int len,
|
||||||
|
const cpumask_t *srcp, int nbits)
|
||||||
|
{
|
||||||
|
return bitmap_scnprintf(buf, len, srcp->bits, nbits);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define cpumask_parse_user(ubuf, ulen, dst) \
|
||||||
|
__cpumask_parse_user((ubuf), (ulen), &(dst), NR_CPUS)
|
||||||
|
static inline int __cpumask_parse_user(const char *buf, int len,
|
||||||
|
cpumask_t *dstp, int nbits)
|
||||||
|
{
|
||||||
|
return bitmap_parse(buf, len, dstp->bits, nbits);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define cpulist_scnprintf(buf, len, src) \
|
||||||
|
__cpulist_scnprintf((buf), (len), &(src), NR_CPUS)
|
||||||
|
static inline int __cpulist_scnprintf(char *buf, int len,
|
||||||
|
const cpumask_t *srcp, int nbits)
|
||||||
|
{
|
||||||
|
return bitmap_scnlistprintf(buf, len, srcp->bits, nbits);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define cpulist_parse(buf, dst) __cpulist_parse((buf), &(dst), NR_CPUS)
|
||||||
|
static inline int __cpulist_parse(const char *buf, cpumask_t *dstp, int nbits)
|
||||||
|
{
|
||||||
|
return bitmap_parselist(buf, dstp->bits, nbits);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define cpu_remap(oldbit, old, new) \
|
||||||
|
__cpu_remap((oldbit), &(old), &(new), NR_CPUS)
|
||||||
|
static inline int __cpu_remap(int oldbit,
|
||||||
|
const cpumask_t *oldp, const cpumask_t *newp, int nbits)
|
||||||
|
{
|
||||||
|
return bitmap_bitremap(oldbit, oldp->bits, newp->bits, nbits);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define cpus_remap(dst, src, old, new) \
|
||||||
|
__cpus_remap(&(dst), &(src), &(old), &(new), NR_CPUS)
|
||||||
|
static inline void __cpus_remap(cpumask_t *dstp, const cpumask_t *srcp,
|
||||||
|
const cpumask_t *oldp, const cpumask_t *newp, int nbits)
|
||||||
|
{
|
||||||
|
bitmap_remap(dstp->bits, srcp->bits, oldp->bits, newp->bits, nbits);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if NR_CPUS > 1
|
||||||
|
#define for_each_cpu_mask(cpu, mask) \
|
||||||
|
for ((cpu) = first_cpu(mask); \
|
||||||
|
(cpu) < NR_CPUS; \
|
||||||
|
(cpu) = next_cpu((cpu), (mask)))
|
||||||
|
#else /* NR_CPUS == 1 */
|
||||||
|
#define for_each_cpu_mask(cpu, mask) \
|
||||||
|
for ((cpu) = 0; (cpu) < 1; (cpu)++, (void)mask)
|
||||||
|
#endif /* NR_CPUS */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The following particular system cpumasks and operations manage
|
||||||
|
* possible, present and online cpus. Each of them is a fixed size
|
||||||
|
* bitmap of size NR_CPUS.
|
||||||
|
*
|
||||||
|
* #ifdef CONFIG_HOTPLUG_CPU
|
||||||
|
* cpu_possible_map - has bit 'cpu' set iff cpu is populatable
|
||||||
|
* cpu_present_map - has bit 'cpu' set iff cpu is populated
|
||||||
|
* cpu_online_map - has bit 'cpu' set iff cpu available to scheduler
|
||||||
|
* #else
|
||||||
|
* cpu_possible_map - has bit 'cpu' set iff cpu is populated
|
||||||
|
* cpu_present_map - copy of cpu_possible_map
|
||||||
|
* cpu_online_map - has bit 'cpu' set iff cpu available to scheduler
|
||||||
|
* #endif
|
||||||
|
*
|
||||||
|
* In either case, NR_CPUS is fixed at compile time, as the static
|
||||||
|
* size of these bitmaps. The cpu_possible_map is fixed at boot
|
||||||
|
* time, as the set of CPU id's that it is possible might ever
|
||||||
|
* be plugged in at anytime during the life of that system boot.
|
||||||
|
* The cpu_present_map is dynamic(*), representing which CPUs
|
||||||
|
* are currently plugged in. And cpu_online_map is the dynamic
|
||||||
|
* subset of cpu_present_map, indicating those CPUs available
|
||||||
|
* for scheduling.
|
||||||
|
*
|
||||||
|
* If HOTPLUG is enabled, then cpu_possible_map is forced to have
|
||||||
|
* all NR_CPUS bits set, otherwise it is just the set of CPUs that
|
||||||
|
* ACPI reports present at boot.
|
||||||
|
*
|
||||||
|
* If HOTPLUG is enabled, then cpu_present_map varies dynamically,
|
||||||
|
* depending on what ACPI reports as currently plugged in, otherwise
|
||||||
|
* cpu_present_map is just a copy of cpu_possible_map.
|
||||||
|
*
|
||||||
|
* (*) Well, cpu_present_map is dynamic in the hotplug case. If not
|
||||||
|
* hotplug, it's a copy of cpu_possible_map, hence fixed at boot.
|
||||||
|
*
|
||||||
|
* Subtleties:
|
||||||
|
* 1) UP arch's (NR_CPUS == 1, CONFIG_SMP not defined) hardcode
|
||||||
|
* assumption that their single CPU is online. The UP
|
||||||
|
* cpu_{online,possible,present}_maps are placebos. Changing them
|
||||||
|
* will have no useful affect on the following num_*_cpus()
|
||||||
|
* and cpu_*() macros in the UP case. This ugliness is a UP
|
||||||
|
* optimization - don't waste any instructions or memory references
|
||||||
|
* asking if you're online or how many CPUs there are if there is
|
||||||
|
* only one CPU.
|
||||||
|
* 2) Most SMP arch's #define some of these maps to be some
|
||||||
|
* other map specific to that arch. Therefore, the following
|
||||||
|
* must be #define macros, not inlines. To see why, examine
|
||||||
|
* the assembly code produced by the following. Note that
|
||||||
|
* set1() writes phys_x_map, but set2() writes x_map:
|
||||||
|
* int x_map, phys_x_map;
|
||||||
|
* #define set1(a) x_map = a
|
||||||
|
* inline void set2(int a) { x_map = a; }
|
||||||
|
* #define x_map phys_x_map
|
||||||
|
* main(){ set1(3); set2(5); }
|
||||||
|
*/
|
||||||
|
|
||||||
|
extern cpumask_t cpu_possible_map;
|
||||||
|
extern cpumask_t cpu_online_map;
|
||||||
|
extern cpumask_t cpu_present_map;
|
||||||
|
|
||||||
|
#if NR_CPUS > 1
|
||||||
|
#define num_online_cpus() cpus_weight(cpu_online_map)
|
||||||
|
#define num_possible_cpus() cpus_weight(cpu_possible_map)
|
||||||
|
#define num_present_cpus() cpus_weight(cpu_present_map)
|
||||||
|
#define cpu_online(cpu) cpu_isset((cpu), cpu_online_map)
|
||||||
|
#define cpu_possible(cpu) cpu_isset((cpu), cpu_possible_map)
|
||||||
|
#define cpu_present(cpu) cpu_isset((cpu), cpu_present_map)
|
||||||
|
#else
|
||||||
|
#define num_online_cpus() 1
|
||||||
|
#define num_possible_cpus() 1
|
||||||
|
#define num_present_cpus() 1
|
||||||
|
#define cpu_online(cpu) ((cpu) == 0)
|
||||||
|
#define cpu_possible(cpu) ((cpu) == 0)
|
||||||
|
#define cpu_present(cpu) ((cpu) == 0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int highest_possible_processor_id(void);
|
||||||
|
#define any_online_cpu(mask) __any_online_cpu(&(mask))
|
||||||
|
int __any_online_cpu(const cpumask_t *mask);
|
||||||
|
|
||||||
|
#define for_each_possible_cpu(cpu) for_each_cpu_mask((cpu), cpu_possible_map)
|
||||||
|
#define for_each_online_cpu(cpu) for_each_cpu_mask((cpu), cpu_online_map)
|
||||||
|
#define for_each_present_cpu(cpu) for_each_cpu_mask((cpu), cpu_present_map)
|
||||||
|
|
||||||
|
#endif /* __LINUX_CPUMASK_H */
|
|
@ -0,0 +1,371 @@
|
||||||
|
/*
|
||||||
|
* 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)) {
|
||||||
|
free(cpu);
|
||||||
|
/* even though we don't use the cpu we do need to count it */
|
||||||
|
core_count++;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* try to read the package mask; if it doesn't exist assume solitary */
|
||||||
|
snprintf(new_path, PATH_MAX, "%s/topology/core_siblings", path);
|
||||||
|
file = fopen(new_path, "r");
|
||||||
|
cpu_set(cpu->number, cpu->package_mask);
|
||||||
|
if (file) {
|
||||||
|
char line[4096];
|
||||||
|
line[4095]=0;
|
||||||
|
if (fgets(line, 4095, file))
|
||||||
|
cpumask_parse_user(line, strlen(line), cpu->package_mask);
|
||||||
|
fclose(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* try to read the cache mask; if it doesn't exist assume solitary */
|
||||||
|
/* We want the deepest cache level available so try index1 first, then index2 */
|
||||||
|
cpu_set(cpu->number, cpu->cache_mask);
|
||||||
|
snprintf(new_path, PATH_MAX, "%s/cache/index1/shared_cpu_map", path);
|
||||||
|
file = fopen(new_path, "r");
|
||||||
|
if (file) {
|
||||||
|
char line[4096];
|
||||||
|
line[4095]=0;
|
||||||
|
if (fgets(line, 4095, file))
|
||||||
|
cpumask_parse_user(line, strlen(line), cpu->cache_mask);
|
||||||
|
fclose(file);
|
||||||
|
}
|
||||||
|
snprintf(new_path, PATH_MAX, "%s/cache/index2/shared_cpu_map", path);
|
||||||
|
file = fopen(new_path, "r");
|
||||||
|
if (file) {
|
||||||
|
char line[4096];
|
||||||
|
line[4095]=0;
|
||||||
|
if (fgets(line, 4095, file))
|
||||||
|
cpumask_parse_user(line, strlen(line), cpu->cache_mask);
|
||||||
|
fclose(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
blank out the banned cpus from the various masks so that interrupts
|
||||||
|
will never be told to go there
|
||||||
|
*/
|
||||||
|
cpus_and(cpu->cache_mask, cpu->cache_mask, unbanned_cpus);
|
||||||
|
cpus_and(cpu->package_mask, cpu->package_mask, unbanned_cpus);
|
||||||
|
cpus_and(cpu->mask, cpu->mask, unbanned_cpus);
|
||||||
|
|
||||||
|
cpus = g_list_append(cpus, cpu);
|
||||||
|
core_count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dump_irqs(int spaces, GList *interrupts)
|
||||||
|
{
|
||||||
|
struct interrupt *irq;
|
||||||
|
while (interrupts) {
|
||||||
|
int i;
|
||||||
|
for (i=0; i<spaces;i++) printf(" ");
|
||||||
|
irq = interrupts->data;
|
||||||
|
printf("Interrupt %i (%s/%u) \n", irq->number, classes[irq->class], (unsigned int)irq->workload);
|
||||||
|
interrupts = g_list_next(interrupts);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void dump_tree(void)
|
||||||
|
{
|
||||||
|
GList *p_iter, *c_iter, *cp_iter;
|
||||||
|
struct package *package;
|
||||||
|
struct cache_domain *cache_domain;
|
||||||
|
struct cpu_core *cpu;
|
||||||
|
|
||||||
|
char buffer[4096];
|
||||||
|
p_iter = g_list_first(packages);
|
||||||
|
while (p_iter) {
|
||||||
|
package = p_iter->data;
|
||||||
|
cpumask_scnprintf(buffer, 4096, package->mask);
|
||||||
|
printf("Package %i: cpu mask is %s (workload %lu)\n", package->number, buffer, (unsigned long)package->workload);
|
||||||
|
c_iter = g_list_first(package->cache_domains);
|
||||||
|
while (c_iter) {
|
||||||
|
cache_domain = c_iter->data;
|
||||||
|
c_iter = g_list_next(c_iter);
|
||||||
|
cpumask_scnprintf(buffer, 4095, cache_domain->mask);
|
||||||
|
printf(" Cache domain %i: cpu mask is %s (workload %lu) \n", cache_domain->number, buffer, (unsigned long)cache_domain->workload);
|
||||||
|
cp_iter = cache_domain->cpu_cores;
|
||||||
|
while (cp_iter) {
|
||||||
|
cpu = cp_iter->data;
|
||||||
|
cp_iter = g_list_next(cp_iter);
|
||||||
|
printf(" CPU number %i (workload %lu)\n", cpu->number, (unsigned long)cpu->workload);
|
||||||
|
dump_irqs(18, cpu->interrupts);
|
||||||
|
}
|
||||||
|
dump_irqs(10, cache_domain->interrupts);
|
||||||
|
}
|
||||||
|
dump_irqs(2, package->interrupts);
|
||||||
|
p_iter = g_list_next(p_iter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* this function removes previous state from the cpu tree, such as
|
||||||
|
* which level does how much work and the actual lists of interrupts
|
||||||
|
* assigned to each component
|
||||||
|
*/
|
||||||
|
void clear_work_stats(void)
|
||||||
|
{
|
||||||
|
GList *p_iter, *c_iter, *cp_iter;
|
||||||
|
struct package *package;
|
||||||
|
struct cache_domain *cache_domain;
|
||||||
|
struct cpu_core *cpu;
|
||||||
|
|
||||||
|
p_iter = g_list_first(packages);
|
||||||
|
while (p_iter) {
|
||||||
|
package = p_iter->data;
|
||||||
|
package->workload = 0;
|
||||||
|
g_list_free(package->interrupts);
|
||||||
|
package->interrupts = NULL;
|
||||||
|
c_iter = g_list_first(package->cache_domains);
|
||||||
|
memset(package->class_count, 0, sizeof(package->class_count));
|
||||||
|
while (c_iter) {
|
||||||
|
cache_domain = c_iter->data;
|
||||||
|
c_iter = g_list_next(c_iter);
|
||||||
|
cache_domain->workload = 0;
|
||||||
|
cp_iter = cache_domain->cpu_cores;
|
||||||
|
g_list_free(cache_domain->interrupts);
|
||||||
|
cache_domain->interrupts = NULL;
|
||||||
|
memset(cache_domain->class_count, 0, sizeof(cache_domain->class_count));
|
||||||
|
while (cp_iter) {
|
||||||
|
cpu = cp_iter->data;
|
||||||
|
cp_iter = g_list_next(cp_iter);
|
||||||
|
cpu->workload = 0;
|
||||||
|
g_list_free(cpu->interrupts);
|
||||||
|
cpu->interrupts = NULL;
|
||||||
|
memset(cpu->class_count, 0, sizeof(cpu->class_count));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p_iter = g_list_next(p_iter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void parse_cpu_tree(void)
|
||||||
|
{
|
||||||
|
DIR *dir;
|
||||||
|
struct dirent *entry;
|
||||||
|
|
||||||
|
cpus_complement(unbanned_cpus, banned_cpus);
|
||||||
|
|
||||||
|
dir = opendir("/sys/devices/system/cpu");
|
||||||
|
if (!dir)
|
||||||
|
return;
|
||||||
|
do {
|
||||||
|
entry = readdir(dir);
|
||||||
|
if (entry && strlen(entry->d_name)>3 && strstr(entry->d_name,"cpu")) {
|
||||||
|
char new_path[PATH_MAX];
|
||||||
|
sprintf(new_path, "/sys/devices/system/cpu/%s", entry->d_name);
|
||||||
|
do_one_cpu(new_path);
|
||||||
|
}
|
||||||
|
} while (entry);
|
||||||
|
closedir(dir);
|
||||||
|
|
||||||
|
fill_cache_domain();
|
||||||
|
fill_packages();
|
||||||
|
|
||||||
|
if (debug_mode)
|
||||||
|
dump_tree();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This function frees all memory related to a cpu tree so that a new tree
|
||||||
|
* can be read
|
||||||
|
*/
|
||||||
|
void clear_cpu_tree(void)
|
||||||
|
{
|
||||||
|
GList *item;
|
||||||
|
struct cpu_core *cpu;
|
||||||
|
struct cache_domain *cache_domain;
|
||||||
|
struct package *package;
|
||||||
|
|
||||||
|
while (packages) {
|
||||||
|
item = g_list_first(packages);
|
||||||
|
package = item->data;
|
||||||
|
g_list_free(package->cache_domains);
|
||||||
|
g_list_free(package->interrupts);
|
||||||
|
free(package);
|
||||||
|
packages = g_list_delete_link(packages, item);
|
||||||
|
}
|
||||||
|
package_count = 0;
|
||||||
|
|
||||||
|
while (cache_domains) {
|
||||||
|
item = g_list_first(cache_domains);
|
||||||
|
cache_domain = item->data;
|
||||||
|
g_list_free(cache_domain->cpu_cores);
|
||||||
|
g_list_free(cache_domain->interrupts);
|
||||||
|
free(cache_domain);
|
||||||
|
cache_domains = g_list_delete_link(cache_domains, item);
|
||||||
|
}
|
||||||
|
cache_domain_count = 0;
|
||||||
|
|
||||||
|
|
||||||
|
while (cpus) {
|
||||||
|
item = g_list_first(cpus);
|
||||||
|
cpu = item->data;
|
||||||
|
g_list_free(cpu->interrupts);
|
||||||
|
free(cpu);
|
||||||
|
cpus = g_list_delete_link(cpus, item);
|
||||||
|
}
|
||||||
|
core_count = 0;
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,138 @@
|
||||||
|
/*
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <malloc.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
|
||||||
|
#include "irqbalance.h"
|
||||||
|
|
||||||
|
int one_shot_mode;
|
||||||
|
int debug_mode;
|
||||||
|
|
||||||
|
int need_cpu_rescan;
|
||||||
|
|
||||||
|
extern cpumask_t banned_cpus;
|
||||||
|
|
||||||
|
static int counter;
|
||||||
|
|
||||||
|
|
||||||
|
void sleep_approx(int seconds)
|
||||||
|
{
|
||||||
|
struct timespec ts;
|
||||||
|
struct timeval tv;
|
||||||
|
gettimeofday(&tv, NULL);
|
||||||
|
ts.tv_sec = seconds;
|
||||||
|
ts.tv_nsec = -tv.tv_usec*1000;
|
||||||
|
while (ts.tv_nsec < 0) {
|
||||||
|
ts.tv_sec--;
|
||||||
|
ts.tv_nsec += 1000000000;
|
||||||
|
}
|
||||||
|
nanosleep(&ts, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char** argv)
|
||||||
|
{
|
||||||
|
if (argc>1 && strstr(argv[1],"debug"))
|
||||||
|
debug_mode=1;
|
||||||
|
if (argc>1 && strstr(argv[1],"oneshot"))
|
||||||
|
one_shot_mode=1;
|
||||||
|
|
||||||
|
if (getenv("IRQBALANCE_BANNED_CPUS")) {
|
||||||
|
cpumask_parse_user(getenv("IRQBALANCE_BANNED_CPUS"), strlen(getenv("IRQBALANCE_BANNED_CPUS")), banned_cpus);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (getenv("IRQBALANCE_ONESHOT"))
|
||||||
|
one_shot_mode=1;
|
||||||
|
|
||||||
|
if (getenv("IRQBALANCE_DEBUG"))
|
||||||
|
debug_mode=1;
|
||||||
|
|
||||||
|
parse_cpu_tree();
|
||||||
|
|
||||||
|
|
||||||
|
/* On single core UP systems irqbalance obviously has no work to do */
|
||||||
|
if (core_count<2)
|
||||||
|
exit(EXIT_SUCCESS);
|
||||||
|
/* On dual core/hyperthreading shared cache systems just do a one shot setup */
|
||||||
|
if (cache_domain_count==1)
|
||||||
|
one_shot_mode = 1;
|
||||||
|
|
||||||
|
|
||||||
|
if (!debug_mode)
|
||||||
|
if (daemon(0,0))
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
|
||||||
|
parse_proc_interrupts();
|
||||||
|
sleep(SLEEP_INTERVAL/4);
|
||||||
|
reset_counts();
|
||||||
|
parse_proc_interrupts();
|
||||||
|
pci_numa_scan();
|
||||||
|
calculate_workload();
|
||||||
|
sort_irq_list();
|
||||||
|
if (debug_mode)
|
||||||
|
dump_workloads();
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
sleep_approx(SLEEP_INTERVAL);
|
||||||
|
if (debug_mode)
|
||||||
|
printf("\n\n\n-----------------------------------------------------------------------------\n");
|
||||||
|
|
||||||
|
|
||||||
|
check_power_mode();
|
||||||
|
parse_proc_interrupts();
|
||||||
|
|
||||||
|
/* cope with cpu hotplug -- detected during /proc/interrupts parsing */
|
||||||
|
if (need_cpu_rescan) {
|
||||||
|
need_cpu_rescan = 0;
|
||||||
|
/* if there's a hotplug event we better turn off power mode for a bit until things settle */
|
||||||
|
power_mode = 0;
|
||||||
|
if (debug_mode)
|
||||||
|
printf("Rescanning cpu topology \n");
|
||||||
|
reset_counts();
|
||||||
|
clear_work_stats();
|
||||||
|
|
||||||
|
clear_cpu_tree();
|
||||||
|
parse_cpu_tree();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* deal with NAPI */
|
||||||
|
account_for_nic_stats();
|
||||||
|
calculate_workload();
|
||||||
|
|
||||||
|
/* to cope with dynamic configurations we scan for new numa information
|
||||||
|
* once every 5 minutes
|
||||||
|
*/
|
||||||
|
if (counter % NUMA_REFRESH_INTERVAL == 16)
|
||||||
|
pci_numa_scan();
|
||||||
|
|
||||||
|
calculate_placement();
|
||||||
|
activate_mapping();
|
||||||
|
|
||||||
|
if (debug_mode)
|
||||||
|
dump_tree();
|
||||||
|
if (one_shot_mode)
|
||||||
|
break;
|
||||||
|
counter++;
|
||||||
|
}
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
|
@ -0,0 +1,50 @@
|
||||||
|
#ifndef __INCLUDE_GUARD_IRQBALANCE_H_
|
||||||
|
#define __INCLUDE_GUARD_IRQBALANCE_H_
|
||||||
|
|
||||||
|
|
||||||
|
#include "constants.h"
|
||||||
|
|
||||||
|
#include "cpumask.h"
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <glib.h>
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
|
||||||
|
struct interrupt;
|
||||||
|
|
||||||
|
extern int package_count;
|
||||||
|
extern int cache_domain_count;
|
||||||
|
extern int core_count;
|
||||||
|
extern char *classes[];
|
||||||
|
extern int map_class_to_level[7];
|
||||||
|
extern int class_counts[7];
|
||||||
|
extern int debug_mode;
|
||||||
|
extern int power_mode;
|
||||||
|
extern int need_cpu_rescan;
|
||||||
|
extern int one_shot_mode;
|
||||||
|
extern GList *interrupts;
|
||||||
|
|
||||||
|
|
||||||
|
extern void parse_cpu_tree(void);
|
||||||
|
extern void clear_work_stats(void);
|
||||||
|
extern void parse_proc_interrupts(void);
|
||||||
|
extern void set_interrupt_count(int number, uint64_t count, cpumask_t *mask);
|
||||||
|
extern void add_interrupt_count(int number, uint64_t count, int type);
|
||||||
|
extern int find_class(struct interrupt *irq, char *string);
|
||||||
|
extern void add_interrupt_numa(int number, cpumask_t mask, int type);
|
||||||
|
|
||||||
|
void calculate_workload(void);
|
||||||
|
void reset_counts(void);
|
||||||
|
void dump_workloads(void);
|
||||||
|
void sort_irq_list(void);
|
||||||
|
void calculate_placement(void);
|
||||||
|
void dump_tree(void);
|
||||||
|
|
||||||
|
void activate_mapping(void);
|
||||||
|
void account_for_nic_stats(void);
|
||||||
|
void check_power_mode(void);
|
||||||
|
void clear_cpu_tree(void);
|
||||||
|
void pci_numa_scan(void);
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,252 @@
|
||||||
|
/*
|
||||||
|
* 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 has the basic functions to manipulate interrupt metadata
|
||||||
|
*/
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <dirent.h>
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
#include "irqbalance.h"
|
||||||
|
|
||||||
|
GList *interrupts;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This function classifies and reads various things from /proc about a specific irq
|
||||||
|
*/
|
||||||
|
static void investigate(struct interrupt *irq, int number)
|
||||||
|
{
|
||||||
|
DIR *dir;
|
||||||
|
struct dirent *entry;
|
||||||
|
char *c, *c2;
|
||||||
|
int nr;
|
||||||
|
char buf[PATH_MAX];
|
||||||
|
sprintf(buf, "/proc/irq/%i", number);
|
||||||
|
dir = opendir(buf);
|
||||||
|
do {
|
||||||
|
entry = readdir(dir);
|
||||||
|
if (!entry)
|
||||||
|
break;
|
||||||
|
if (strcmp(entry->d_name,"smp_affinity")==0) {
|
||||||
|
FILE *file;
|
||||||
|
sprintf(buf, "/proc/irq/%i/smp_affinity", number);
|
||||||
|
file = fopen(buf, "r");
|
||||||
|
if (!file)
|
||||||
|
continue;
|
||||||
|
if (fgets(buf, PATH_MAX, file)==NULL) {
|
||||||
|
fclose(file);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
cpumask_parse_user(buf, strlen(buf), irq->mask);
|
||||||
|
fclose(file);
|
||||||
|
} else {
|
||||||
|
irq->class = find_class(irq, entry->d_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
} while (entry);
|
||||||
|
closedir(dir);
|
||||||
|
irq->balance_level = map_class_to_level[irq->class];
|
||||||
|
|
||||||
|
/* next, check the IRQBALANCE_BANNED_INTERRUPTS env variable for blacklisted irqs */
|
||||||
|
c = getenv("IRQBALANCE_BANNED_INTERRUPTS");
|
||||||
|
if (!c)
|
||||||
|
return;
|
||||||
|
|
||||||
|
do {
|
||||||
|
nr = strtoul(c, &c2, 10);
|
||||||
|
if (c!=c2 && nr == number)
|
||||||
|
irq->balance_level = BALANCE_NONE;
|
||||||
|
c = c2;
|
||||||
|
} while (c!=c2 && c2!=NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set the number of interrupts received for a specific irq;
|
||||||
|
* create the irq metadata if there is none yet
|
||||||
|
*/
|
||||||
|
void set_interrupt_count(int number, uint64_t count, cpumask_t *mask)
|
||||||
|
{
|
||||||
|
GList *item;
|
||||||
|
struct interrupt *irq;
|
||||||
|
|
||||||
|
if (count < MIN_IRQ_COUNT && !one_shot_mode)
|
||||||
|
return; /* no need to track or set interrupts sources without any activity since boot
|
||||||
|
but allow for a few (20) boot-time-only interrupts */
|
||||||
|
|
||||||
|
item = g_list_first(interrupts);
|
||||||
|
while (item) {
|
||||||
|
irq = item->data;
|
||||||
|
|
||||||
|
if (irq->number == number) {
|
||||||
|
irq->count = count;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
item = g_list_next(item);
|
||||||
|
}
|
||||||
|
/* new interrupt */
|
||||||
|
irq = malloc(sizeof(struct interrupt));
|
||||||
|
if (!irq)
|
||||||
|
return;
|
||||||
|
memset(irq, 0, sizeof(struct interrupt));
|
||||||
|
irq->number = number;
|
||||||
|
irq->count = count;
|
||||||
|
investigate(irq, number);
|
||||||
|
if (irq->balance_level == BALANCE_NONE)
|
||||||
|
irq->mask = *mask;
|
||||||
|
|
||||||
|
interrupts = g_list_append(interrupts, irq);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add extra irqs to a specific irq metadata structure;
|
||||||
|
* if no such metadata exists, do nothing at all
|
||||||
|
*/
|
||||||
|
void add_interrupt_count(int number, uint64_t count, int type)
|
||||||
|
{
|
||||||
|
GList *item;
|
||||||
|
struct interrupt *irq;
|
||||||
|
|
||||||
|
if (!count)
|
||||||
|
return;
|
||||||
|
|
||||||
|
item = g_list_first(interrupts);
|
||||||
|
while (item) {
|
||||||
|
irq = item->data;
|
||||||
|
item = g_list_next(item);
|
||||||
|
|
||||||
|
if (irq->number == number) {
|
||||||
|
irq->extra += count;
|
||||||
|
if (irq->class < type && irq->balance_level != BALANCE_NONE) {
|
||||||
|
irq->class = type;
|
||||||
|
irq->balance_level = map_class_to_level[irq->class];
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set the numa affinity mask for a specific interrupt if there
|
||||||
|
* is metadata for the interrupt; do nothing if no such data
|
||||||
|
* exists.
|
||||||
|
*/
|
||||||
|
void add_interrupt_numa(int number, cpumask_t mask, int type)
|
||||||
|
{
|
||||||
|
GList *item;
|
||||||
|
struct interrupt *irq;
|
||||||
|
|
||||||
|
item = g_list_first(interrupts);
|
||||||
|
while (item) {
|
||||||
|
irq = item->data;
|
||||||
|
item = g_list_next(item);
|
||||||
|
|
||||||
|
if (irq->number == number) {
|
||||||
|
cpus_or(irq->numa_mask, irq->numa_mask, mask);
|
||||||
|
if (irq->class < type && irq->balance_level != BALANCE_NONE) {
|
||||||
|
irq->class = type;
|
||||||
|
irq->balance_level = map_class_to_level[irq->class];
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void calculate_workload(void)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
GList *item;
|
||||||
|
struct interrupt *irq;
|
||||||
|
|
||||||
|
for (i=0; i<7; i++)
|
||||||
|
class_counts[i]=0;
|
||||||
|
item = g_list_first(interrupts);
|
||||||
|
while (item) {
|
||||||
|
irq = item->data;
|
||||||
|
item = g_list_next(item);
|
||||||
|
|
||||||
|
irq->workload = irq->count - irq->old_count + irq->workload/3 + irq->extra;
|
||||||
|
class_counts[irq->class]++;
|
||||||
|
irq->old_count = irq->count;
|
||||||
|
irq->extra = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void reset_counts(void)
|
||||||
|
{
|
||||||
|
GList *item;
|
||||||
|
struct interrupt *irq;
|
||||||
|
item = g_list_first(interrupts);
|
||||||
|
while (item) {
|
||||||
|
irq = item->data;
|
||||||
|
item = g_list_next(item);
|
||||||
|
irq->old_count = irq->count;
|
||||||
|
irq->extra = 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void dump_workloads(void)
|
||||||
|
{
|
||||||
|
GList *item;
|
||||||
|
struct interrupt *irq;
|
||||||
|
item = g_list_first(interrupts);
|
||||||
|
while (item) {
|
||||||
|
irq = item->data;
|
||||||
|
item = g_list_next(item);
|
||||||
|
|
||||||
|
printf("Interrupt %i (class %s) has workload %lu \n", irq->number, classes[irq->class], (unsigned long)irq->workload);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static gint sort_irqs(gconstpointer A, gconstpointer B)
|
||||||
|
{
|
||||||
|
struct interrupt *a, *b;
|
||||||
|
a = (struct interrupt*)A;
|
||||||
|
b = (struct interrupt*)B;
|
||||||
|
|
||||||
|
if (a->class < b->class)
|
||||||
|
return 1;
|
||||||
|
if (a->class > b->class)
|
||||||
|
return -1;
|
||||||
|
if (a->workload < b->workload)
|
||||||
|
return 1;
|
||||||
|
if (a->workload > b->workload)
|
||||||
|
return -1;
|
||||||
|
if (a<b)
|
||||||
|
return 1;
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void sort_irq_list(void)
|
||||||
|
{
|
||||||
|
/* sort by class first (high->low) and then by workload (high->low) */
|
||||||
|
interrupts = g_list_sort(interrupts, sort_irqs);
|
||||||
|
}
|
|
@ -0,0 +1,175 @@
|
||||||
|
/*
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Due to NAPI, the actual number of interrupts for a network NIC is usually low
|
||||||
|
* even though the amount of work is high; this file is there to compensate for this
|
||||||
|
* by adding actual package counts to the calculated amount of work of interrupts
|
||||||
|
*/
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <linux/types.h>
|
||||||
|
#include <linux/ethtool.h>
|
||||||
|
#include <glib.h>
|
||||||
|
#include <net/if.h>
|
||||||
|
#include <linux/sockios.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
|
||||||
|
#include "irqbalance.h"
|
||||||
|
|
||||||
|
struct nic {
|
||||||
|
char ethname[64];
|
||||||
|
int irq;
|
||||||
|
uint64_t prev_pkt;
|
||||||
|
int counter;
|
||||||
|
};
|
||||||
|
|
||||||
|
static GList *nics;
|
||||||
|
|
||||||
|
|
||||||
|
static int dev_to_irq(char *devname)
|
||||||
|
{
|
||||||
|
int sock, ret;
|
||||||
|
struct ifreq ifr;
|
||||||
|
struct ethtool_value ethtool;
|
||||||
|
struct ethtool_drvinfo driver;
|
||||||
|
FILE *file;
|
||||||
|
|
||||||
|
char buffer[PATH_MAX];
|
||||||
|
|
||||||
|
memset(&ifr, 0, sizeof(struct ifreq));
|
||||||
|
memset(ðtool, 0, sizeof(struct ethtool_value));
|
||||||
|
|
||||||
|
sock = socket(AF_INET, SOCK_DGRAM, 0);
|
||||||
|
if (sock<0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
strcpy(ifr.ifr_name, devname);
|
||||||
|
|
||||||
|
driver.cmd = ETHTOOL_GDRVINFO;
|
||||||
|
ifr.ifr_data = (void*) &driver;
|
||||||
|
ret = ioctl(sock, SIOCETHTOOL, &ifr);
|
||||||
|
close(sock);
|
||||||
|
if (ret<0)
|
||||||
|
return 0;
|
||||||
|
sprintf(buffer,"/sys/bus/pci/devices/%s/irq", driver.bus_info);
|
||||||
|
file = fopen(buffer, "r");
|
||||||
|
if (!file)
|
||||||
|
return 0;
|
||||||
|
if (fgets(buffer, PATH_MAX, file)==NULL)
|
||||||
|
strcpy(buffer,"0");
|
||||||
|
fclose(file);
|
||||||
|
return strtoul(buffer, NULL, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct nic *new_nic(char *name)
|
||||||
|
{
|
||||||
|
struct nic *nic;
|
||||||
|
nic = malloc(sizeof(struct nic));
|
||||||
|
if (!nic)
|
||||||
|
return NULL;
|
||||||
|
memset(nic, 0, sizeof(struct nic));
|
||||||
|
strcpy(nic->ethname, name);
|
||||||
|
nic->irq = dev_to_irq(name);
|
||||||
|
nics = g_list_append(nics, nic);
|
||||||
|
return nic;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct nic *find_nic(char *name)
|
||||||
|
{
|
||||||
|
GList *item;
|
||||||
|
struct nic *nic;
|
||||||
|
item = g_list_first(nics);
|
||||||
|
while (item) {
|
||||||
|
nic = item->data;
|
||||||
|
item = g_list_next(item);
|
||||||
|
if (strcmp(nic->ethname, name)==0) {
|
||||||
|
nic->counter++;
|
||||||
|
/* refresh irq information once in a while; ifup/down
|
||||||
|
* can make this info go stale over time
|
||||||
|
*/
|
||||||
|
if ((nic->counter % NIC_REFRESH_INTERVAL) == 0)
|
||||||
|
nic->irq = dev_to_irq(nic->ethname);
|
||||||
|
return nic;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
nic = new_nic(name);
|
||||||
|
return nic;
|
||||||
|
}
|
||||||
|
|
||||||
|
void account_for_nic_stats(void)
|
||||||
|
{
|
||||||
|
struct nic *nic;
|
||||||
|
FILE *file;
|
||||||
|
char line[8192];
|
||||||
|
file = fopen("/proc/net/dev", "r");
|
||||||
|
if (!file)
|
||||||
|
return;
|
||||||
|
/* first two lines are headers */
|
||||||
|
if (fgets(line, 8191, file)==NULL)
|
||||||
|
return;
|
||||||
|
if (fgets(line, 8191, file)==NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
while (!feof(file)) {
|
||||||
|
uint64_t rxcount;
|
||||||
|
uint64_t txcount;
|
||||||
|
uint64_t delta;
|
||||||
|
int dummy;
|
||||||
|
char *c, *c2;
|
||||||
|
if (fgets(line, 8191, file)==NULL)
|
||||||
|
break;
|
||||||
|
c = strchr(line, ':');
|
||||||
|
if (c==NULL) /* header line */
|
||||||
|
continue;
|
||||||
|
*c = 0;
|
||||||
|
c++;
|
||||||
|
c2 = &line[0];
|
||||||
|
while (*c2==' ') c2++;
|
||||||
|
nic = find_nic(c2);
|
||||||
|
if (!nic)
|
||||||
|
continue;
|
||||||
|
dummy = strtoul(c, &c, 10);
|
||||||
|
rxcount = strtoull(c, &c, 10);
|
||||||
|
dummy = strtoul(c, &c, 10);
|
||||||
|
dummy = strtoul(c, &c, 10);
|
||||||
|
dummy = strtoul(c, &c, 10);
|
||||||
|
dummy = strtoul(c, &c, 10);
|
||||||
|
dummy = strtoul(c, &c, 10);
|
||||||
|
dummy = strtoul(c, &c, 10);
|
||||||
|
dummy = strtoul(c, &c, 10);
|
||||||
|
txcount = strtoull(c, &c, 10);
|
||||||
|
delta = (txcount+rxcount-nic->prev_pkt)/2;
|
||||||
|
/* add the RX and TX packets to the irq count, but only for 50%;
|
||||||
|
many packets generate another IRQ anyway and we don't want to
|
||||||
|
overweigh this too much */
|
||||||
|
if (delta>0 && nic->prev_pkt != 0)
|
||||||
|
add_interrupt_count(nic->irq, delta, IRQ_ETH);
|
||||||
|
nic->prev_pkt = rxcount + txcount;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
fclose(file);
|
||||||
|
}
|
|
@ -0,0 +1,115 @@
|
||||||
|
/*
|
||||||
|
|
||||||
|
This file is copied from the Linux kernel and mildly adjusted for use in userspace
|
||||||
|
|
||||||
|
|
||||||
|
*/
|
||||||
|
#ifndef _ASM_GENERIC_BITOPS_NON_ATOMIC_H_
|
||||||
|
#define _ASM_GENERIC_BITOPS_NON_ATOMIC_H_
|
||||||
|
|
||||||
|
#define BITOP_MASK(nr) (1UL << ((nr) % BITS_PER_LONG))
|
||||||
|
#define BITOP_WORD(nr) ((nr) / BITS_PER_LONG)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* __set_bit - Set a bit in memory
|
||||||
|
* @nr: the bit to set
|
||||||
|
* @addr: the address to start counting from
|
||||||
|
*
|
||||||
|
* Unlike set_bit(), this function is non-atomic and may be reordered.
|
||||||
|
* If it's called on the same region of memory simultaneously, the effect
|
||||||
|
* may be that only one operation succeeds.
|
||||||
|
*/
|
||||||
|
static inline void set_bit(int nr, volatile unsigned long *addr)
|
||||||
|
{
|
||||||
|
unsigned long mask = BITOP_MASK(nr);
|
||||||
|
unsigned long *p = ((unsigned long *)addr) + BITOP_WORD(nr);
|
||||||
|
|
||||||
|
*p |= mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void clear_bit(int nr, volatile unsigned long *addr)
|
||||||
|
{
|
||||||
|
unsigned long mask = BITOP_MASK(nr);
|
||||||
|
unsigned long *p = ((unsigned long *)addr) + BITOP_WORD(nr);
|
||||||
|
|
||||||
|
*p &= ~mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* __change_bit - Toggle a bit in memory
|
||||||
|
* @nr: the bit to change
|
||||||
|
* @addr: the address to start counting from
|
||||||
|
*
|
||||||
|
* Unlike change_bit(), this function is non-atomic and may be reordered.
|
||||||
|
* If it's called on the same region of memory simultaneously, the effect
|
||||||
|
* may be that only one operation succeeds.
|
||||||
|
*/
|
||||||
|
static inline void __change_bit(int nr, volatile unsigned long *addr)
|
||||||
|
{
|
||||||
|
unsigned long mask = BITOP_MASK(nr);
|
||||||
|
unsigned long *p = ((unsigned long *)addr) + BITOP_WORD(nr);
|
||||||
|
|
||||||
|
*p ^= mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* __test_and_set_bit - Set a bit and return its old value
|
||||||
|
* @nr: Bit to set
|
||||||
|
* @addr: Address to count from
|
||||||
|
*
|
||||||
|
* This operation is non-atomic and can be reordered.
|
||||||
|
* If two examples of this operation race, one can appear to succeed
|
||||||
|
* but actually fail. You must protect multiple accesses with a lock.
|
||||||
|
*/
|
||||||
|
static inline int __test_and_set_bit(int nr, volatile unsigned long *addr)
|
||||||
|
{
|
||||||
|
unsigned long mask = BITOP_MASK(nr);
|
||||||
|
unsigned long *p = ((unsigned long *)addr) + BITOP_WORD(nr);
|
||||||
|
unsigned long old = *p;
|
||||||
|
|
||||||
|
*p = old | mask;
|
||||||
|
return (old & mask) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* __test_and_clear_bit - Clear a bit and return its old value
|
||||||
|
* @nr: Bit to clear
|
||||||
|
* @addr: Address to count from
|
||||||
|
*
|
||||||
|
* This operation is non-atomic and can be reordered.
|
||||||
|
* If two examples of this operation race, one can appear to succeed
|
||||||
|
* but actually fail. You must protect multiple accesses with a lock.
|
||||||
|
*/
|
||||||
|
static inline int __test_and_clear_bit(int nr, volatile unsigned long *addr)
|
||||||
|
{
|
||||||
|
unsigned long mask = BITOP_MASK(nr);
|
||||||
|
unsigned long *p = ((unsigned long *)addr) + BITOP_WORD(nr);
|
||||||
|
unsigned long old = *p;
|
||||||
|
|
||||||
|
*p = old & ~mask;
|
||||||
|
return (old & mask) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* WARNING: non atomic and it can be reordered! */
|
||||||
|
static inline int __test_and_change_bit(int nr,
|
||||||
|
volatile unsigned long *addr)
|
||||||
|
{
|
||||||
|
unsigned long mask = BITOP_MASK(nr);
|
||||||
|
unsigned long *p = ((unsigned long *)addr) + BITOP_WORD(nr);
|
||||||
|
unsigned long old = *p;
|
||||||
|
|
||||||
|
*p = old ^ mask;
|
||||||
|
return (old & mask) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* test_bit - Determine whether a bit is set
|
||||||
|
* @nr: bit number to test
|
||||||
|
* @addr: Address to start counting from
|
||||||
|
*/
|
||||||
|
static inline int test_bit(int nr, const volatile unsigned long *addr)
|
||||||
|
{
|
||||||
|
return 1UL & (addr[BITOP_WORD(nr)] >> (nr & (BITS_PER_LONG-1)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* _ASM_GENERIC_BITOPS_NON_ATOMIC_H_ */
|
|
@ -0,0 +1,101 @@
|
||||||
|
/*
|
||||||
|
* 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 tries to map numa affinity of pci devices to their interrupts
|
||||||
|
* In addition the PCI class information is used to refine the classification
|
||||||
|
* of interrupt sources
|
||||||
|
*/
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <dirent.h>
|
||||||
|
|
||||||
|
#include "irqbalance.h"
|
||||||
|
|
||||||
|
void pci_numa_scan(void)
|
||||||
|
{
|
||||||
|
DIR *dir;
|
||||||
|
struct dirent *entry;
|
||||||
|
cpumask_t mask;
|
||||||
|
char line[PATH_MAX];
|
||||||
|
FILE *file;
|
||||||
|
int irq;
|
||||||
|
unsigned int class;
|
||||||
|
|
||||||
|
dir = opendir("/sys/bus/pci/devices");
|
||||||
|
if (!dir)
|
||||||
|
return;
|
||||||
|
do {
|
||||||
|
int type;
|
||||||
|
entry = readdir(dir);
|
||||||
|
if (!entry)
|
||||||
|
return;
|
||||||
|
if (strlen(entry->d_name)<3)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
sprintf(line,"/sys/bus/pci/devices/%s/irq", entry->d_name);
|
||||||
|
file = fopen(line, "r");
|
||||||
|
if (!file)
|
||||||
|
continue;
|
||||||
|
if (fgets(line, PATH_MAX, file)==NULL)
|
||||||
|
line[0]=0;
|
||||||
|
fclose(file);
|
||||||
|
irq = strtoul(line, NULL, 10);
|
||||||
|
if (!irq)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
sprintf(line,"/sys/bus/pci/devices/%s/class", entry->d_name);
|
||||||
|
file = fopen(line, "r");
|
||||||
|
if (!file)
|
||||||
|
continue;
|
||||||
|
if (fgets(line, PATH_MAX, file)==NULL)
|
||||||
|
line[0]=0;
|
||||||
|
fclose(file);
|
||||||
|
class = strtoul(line, NULL, 16);
|
||||||
|
|
||||||
|
sprintf(line,"/sys/bus/pci/devices/%s/local_cpus", entry->d_name);
|
||||||
|
file = fopen(line, "r");
|
||||||
|
if (!file)
|
||||||
|
continue;
|
||||||
|
if (fgets(line, PATH_MAX, file)==NULL)
|
||||||
|
line[0]=0;
|
||||||
|
fclose(file);
|
||||||
|
cpumask_parse_user(line, strlen(line), mask);
|
||||||
|
|
||||||
|
type = IRQ_OTHER;
|
||||||
|
if ((class>>16) == 0x01)
|
||||||
|
type = IRQ_SCSI;
|
||||||
|
/*
|
||||||
|
* Ethernet gets the type via /proc/net/dev; in addition down'd interfaces
|
||||||
|
* shouldn't boost interrupts
|
||||||
|
if ((class>>16) == 0x02)
|
||||||
|
type = IRQ_ETH;
|
||||||
|
*/
|
||||||
|
if ((class>>16) >= 0x03 && (class>>16) <= 0x0C)
|
||||||
|
type = IRQ_LEGACY;
|
||||||
|
|
||||||
|
add_interrupt_numa(irq, mask, type);
|
||||||
|
|
||||||
|
} while (entry);
|
||||||
|
closedir(dir);
|
||||||
|
}
|
|
@ -0,0 +1,315 @@
|
||||||
|
/*
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
#include "irqbalance.h"
|
||||||
|
|
||||||
|
|
||||||
|
int power_mode;
|
||||||
|
|
||||||
|
extern GList *interrupts, *packages, *cache_domains, *cpus;
|
||||||
|
|
||||||
|
static uint64_t package_cost_func(struct interrupt *irq, struct package *package)
|
||||||
|
{
|
||||||
|
int bonus = 0;
|
||||||
|
int maxcount;
|
||||||
|
/* moving to a cold package/cache/etc gets you a 3000 penalty */
|
||||||
|
if (!cpus_intersects(irq->old_mask, package->mask))
|
||||||
|
bonus = CROSS_PACKAGE_PENALTY;
|
||||||
|
|
||||||
|
/* do a little numa affinity */
|
||||||
|
if (!cpus_intersects(irq->numa_mask, package->mask))
|
||||||
|
bonus += NUMA_PENALTY;
|
||||||
|
|
||||||
|
/* but if the irq has had 0 interrupts for a while move it about more easily */
|
||||||
|
if (irq->workload==0)
|
||||||
|
bonus = bonus / 10;
|
||||||
|
|
||||||
|
/* in power save mode, you better be on package 0, with overflow to the next package if really needed */
|
||||||
|
if (power_mode)
|
||||||
|
bonus += POWER_MODE_PACKAGE_THRESHOLD * package->number;
|
||||||
|
|
||||||
|
/* if we're out of whack in terms of per class counts.. just block (except in power mode) */
|
||||||
|
maxcount = (class_counts[irq->class] + package_count -1 ) / package_count;
|
||||||
|
if (package->class_count[irq->class]>=maxcount && !power_mode)
|
||||||
|
bonus += 300000;
|
||||||
|
|
||||||
|
return irq->workload + bonus;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint64_t cache_domain_cost_func(struct interrupt *irq, struct cache_domain *cache_domain)
|
||||||
|
{
|
||||||
|
int bonus = 0;
|
||||||
|
/* moving to a cold cache gets you a 1500 penalty */
|
||||||
|
if (!cpus_intersects(irq->old_mask, cache_domain->mask))
|
||||||
|
bonus = CROSS_PACKAGE_PENALTY/2;
|
||||||
|
|
||||||
|
/* do a little numa affinity */
|
||||||
|
if (!cpus_intersects(irq->numa_mask, cache_domain->mask))
|
||||||
|
bonus += NUMA_PENALTY;
|
||||||
|
|
||||||
|
/* but if the irq has had 0 interrupts for a while move it about more easily */
|
||||||
|
if (irq->workload==0)
|
||||||
|
bonus = bonus / 10;
|
||||||
|
|
||||||
|
|
||||||
|
/* pay 6000 for each previous interrupt of the same class */
|
||||||
|
bonus += CLASS_VIOLATION_PENTALTY * cache_domain->class_count[irq->class];
|
||||||
|
|
||||||
|
return irq->workload + bonus;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint64_t cpu_cost_func(struct interrupt *irq, struct cpu_core *cpu)
|
||||||
|
{
|
||||||
|
int bonus = 0;
|
||||||
|
/* moving to a colder core gets you a 1000 penalty */
|
||||||
|
if (!cpus_intersects(irq->old_mask, cpu->mask))
|
||||||
|
bonus = CROSS_PACKAGE_PENALTY/3;
|
||||||
|
|
||||||
|
/* do a little numa affinity */
|
||||||
|
if (!cpus_intersects(irq->numa_mask, cpu->mask))
|
||||||
|
bonus += NUMA_PENALTY;
|
||||||
|
|
||||||
|
/* but if the irq has had 0 interrupts for a while move it about more easily */
|
||||||
|
if (irq->workload==0)
|
||||||
|
bonus = bonus / 10;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* since some chipsets only place at the first cpu, give a tiny preference to non-first
|
||||||
|
* cpus for specifically placed interrupts
|
||||||
|
*/
|
||||||
|
if (first_cpu(cpu->cache_mask)==cpu->number)
|
||||||
|
bonus++;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* pay 6000 for each previous interrupt of the same class */
|
||||||
|
bonus += CLASS_VIOLATION_PENTALTY * cpu->class_count[irq->class];
|
||||||
|
|
||||||
|
return irq->workload + bonus;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void place_cache_domain(struct package *package)
|
||||||
|
{
|
||||||
|
GList *iter, *next;
|
||||||
|
GList *pkg;
|
||||||
|
struct interrupt *irq;
|
||||||
|
struct cache_domain *cache_domain;
|
||||||
|
|
||||||
|
|
||||||
|
iter = g_list_first(package->interrupts);
|
||||||
|
while (iter) {
|
||||||
|
struct cache_domain *best = NULL;
|
||||||
|
uint64_t best_cost = INT_MAX;
|
||||||
|
irq = iter->data;
|
||||||
|
|
||||||
|
if (irq->balance_level <= BALANCE_PACKAGE) {
|
||||||
|
iter = g_list_next(iter);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
pkg = g_list_first(package->cache_domains);
|
||||||
|
while (pkg) {
|
||||||
|
uint64_t newload;
|
||||||
|
|
||||||
|
cache_domain = pkg->data;
|
||||||
|
newload = cache_domain->workload + cache_domain_cost_func(irq, cache_domain);
|
||||||
|
if (newload < best_cost) {
|
||||||
|
best = cache_domain;
|
||||||
|
best_cost = newload;
|
||||||
|
}
|
||||||
|
|
||||||
|
pkg = g_list_next(pkg);
|
||||||
|
}
|
||||||
|
if (best) {
|
||||||
|
next = g_list_next(iter);
|
||||||
|
package->interrupts = g_list_delete_link(package->interrupts, iter);
|
||||||
|
|
||||||
|
best->workload += irq->workload + 1;
|
||||||
|
best->interrupts=g_list_append(best->interrupts, irq);
|
||||||
|
best->class_count[irq->class]++;
|
||||||
|
irq->mask = best->mask;
|
||||||
|
iter = next;
|
||||||
|
} else
|
||||||
|
iter = g_list_next(iter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void place_core(struct cache_domain *cache_domain)
|
||||||
|
{
|
||||||
|
GList *iter, *next;
|
||||||
|
GList *pkg;
|
||||||
|
struct interrupt *irq;
|
||||||
|
struct cpu_core *cpu;
|
||||||
|
|
||||||
|
|
||||||
|
iter = g_list_first(cache_domain->interrupts);
|
||||||
|
while (iter) {
|
||||||
|
struct cpu_core *best = NULL;
|
||||||
|
uint64_t best_cost = INT_MAX;
|
||||||
|
irq = iter->data;
|
||||||
|
|
||||||
|
/* if the irq isn't per-core policy and is not very busy, leave it at cache domain level */
|
||||||
|
if (irq->balance_level <= BALANCE_CACHE && irq->workload < CORE_SPECIFIC_THRESHOLD && !one_shot_mode) {
|
||||||
|
iter = g_list_next(iter);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
pkg = g_list_first(cache_domain->cpu_cores);
|
||||||
|
while (pkg) {
|
||||||
|
uint64_t newload;
|
||||||
|
|
||||||
|
cpu = pkg->data;
|
||||||
|
newload = cpu->workload + cpu_cost_func(irq, cpu);
|
||||||
|
if (newload < best_cost) {
|
||||||
|
best = cpu;
|
||||||
|
best_cost = newload;
|
||||||
|
}
|
||||||
|
|
||||||
|
pkg = g_list_next(pkg);
|
||||||
|
}
|
||||||
|
if (best) {
|
||||||
|
next = g_list_next(iter);
|
||||||
|
cache_domain->interrupts = g_list_delete_link(cache_domain->interrupts, iter);
|
||||||
|
|
||||||
|
best->workload += irq->workload + 1;
|
||||||
|
best->interrupts=g_list_append(best->interrupts, irq);
|
||||||
|
best->class_count[irq->class]++;
|
||||||
|
irq->mask = best->mask;
|
||||||
|
iter = next;
|
||||||
|
} else
|
||||||
|
iter = g_list_next(iter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void place_packages(GList *list)
|
||||||
|
{
|
||||||
|
GList *iter;
|
||||||
|
GList *pkg;
|
||||||
|
struct interrupt *irq;
|
||||||
|
struct package *package;
|
||||||
|
|
||||||
|
|
||||||
|
iter = g_list_first(list);
|
||||||
|
while (iter) {
|
||||||
|
struct package *best = NULL;
|
||||||
|
uint64_t best_cost = INT_MAX;
|
||||||
|
irq = iter->data;
|
||||||
|
if (irq->balance_level == BALANCE_NONE) {
|
||||||
|
iter = g_list_next(iter);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
pkg = g_list_first(packages);
|
||||||
|
while (pkg) {
|
||||||
|
uint64_t newload;
|
||||||
|
|
||||||
|
package = pkg->data;
|
||||||
|
newload = package->workload + package_cost_func(irq, package);
|
||||||
|
if (newload < best_cost) {
|
||||||
|
best = package;
|
||||||
|
best_cost = newload;
|
||||||
|
}
|
||||||
|
|
||||||
|
pkg = g_list_next(pkg);
|
||||||
|
}
|
||||||
|
if (best) {
|
||||||
|
best->workload += irq->workload + 1;
|
||||||
|
best->interrupts=g_list_append(best->interrupts, irq);
|
||||||
|
best->class_count[irq->class]++;
|
||||||
|
irq->mask = best->mask;
|
||||||
|
}
|
||||||
|
iter = g_list_next(iter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static void do_unroutables(void)
|
||||||
|
{
|
||||||
|
struct package *package;
|
||||||
|
struct cache_domain *cache_domain;
|
||||||
|
struct cpu_core *cpu;
|
||||||
|
struct interrupt *irq;
|
||||||
|
GList *iter, *inter;
|
||||||
|
|
||||||
|
inter = g_list_first(interrupts);
|
||||||
|
while (inter) {
|
||||||
|
irq = inter->data;
|
||||||
|
inter = g_list_next(inter);
|
||||||
|
if (irq->balance_level != BALANCE_NONE)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
iter = g_list_first(packages);
|
||||||
|
while (iter) {
|
||||||
|
package = iter->data;
|
||||||
|
if (cpus_intersects(package->mask, irq->mask))
|
||||||
|
package->workload += irq->workload;
|
||||||
|
iter = g_list_next(iter);
|
||||||
|
}
|
||||||
|
|
||||||
|
iter = g_list_first(cache_domains);
|
||||||
|
while (iter) {
|
||||||
|
cache_domain = iter->data;
|
||||||
|
if (cpus_intersects(cache_domain->mask, irq->mask))
|
||||||
|
cache_domain->workload += irq->workload;
|
||||||
|
iter = g_list_next(iter);
|
||||||
|
}
|
||||||
|
iter = g_list_first(cpus);
|
||||||
|
while (iter) {
|
||||||
|
cpu = iter->data;
|
||||||
|
if (cpus_intersects(cpu->mask, irq->mask))
|
||||||
|
cpu->workload += irq->workload;
|
||||||
|
iter = g_list_next(iter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void calculate_placement(void)
|
||||||
|
{
|
||||||
|
struct package *package;
|
||||||
|
struct cache_domain *cache_domain;
|
||||||
|
GList *iter;
|
||||||
|
/* first clear old data */
|
||||||
|
clear_work_stats();
|
||||||
|
sort_irq_list();
|
||||||
|
do_unroutables();
|
||||||
|
|
||||||
|
place_packages(interrupts);
|
||||||
|
iter = g_list_first(packages);
|
||||||
|
while (iter) {
|
||||||
|
package = iter->data;
|
||||||
|
place_cache_domain(package);
|
||||||
|
iter = g_list_next(iter);
|
||||||
|
}
|
||||||
|
|
||||||
|
iter = g_list_first(cache_domains);
|
||||||
|
while (iter) {
|
||||||
|
cache_domain = iter->data;
|
||||||
|
place_core(cache_domain);
|
||||||
|
iter = g_list_next(iter);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,75 @@
|
||||||
|
/*
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "irqbalance.h"
|
||||||
|
|
||||||
|
|
||||||
|
extern int power_mode;
|
||||||
|
|
||||||
|
static uint64_t previous;
|
||||||
|
|
||||||
|
static unsigned int hysteresis;
|
||||||
|
|
||||||
|
void check_power_mode(void)
|
||||||
|
{
|
||||||
|
FILE *file;
|
||||||
|
char line[4096];
|
||||||
|
char *c;
|
||||||
|
uint64_t dummy, irq, softirq;
|
||||||
|
line[0]=0;
|
||||||
|
line[4095]=0;
|
||||||
|
file = fopen("/proc/stat", "r");
|
||||||
|
if (!file)
|
||||||
|
return;
|
||||||
|
if (fgets(line, 4095, file)==NULL)
|
||||||
|
memset(line,0, 4096);
|
||||||
|
fclose(file);
|
||||||
|
c=&line[4];
|
||||||
|
dummy = strtoull(c, &c, 10); /* user */
|
||||||
|
dummy = strtoull(c, &c, 10); /* nice */
|
||||||
|
dummy = strtoull(c, &c, 10); /* system */
|
||||||
|
dummy = strtoull(c, &c, 10); /* idle */
|
||||||
|
dummy = strtoull(c, &c, 10); /* iowait */
|
||||||
|
irq = strtoull(c, &c, 10); /* irq */
|
||||||
|
softirq = strtoull(c, &c, 10); /* softirq */
|
||||||
|
|
||||||
|
irq += softirq;
|
||||||
|
if (irq - previous < POWER_MODE_SOFTIRQ_THRESHOLD) {
|
||||||
|
hysteresis++;
|
||||||
|
if (hysteresis > POWER_MODE_HYSTERESIS) {
|
||||||
|
if (debug_mode && !power_mode)
|
||||||
|
printf("IRQ delta is %lu, switching to power mode \n", (unsigned long)(irq - previous) );
|
||||||
|
power_mode = 1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (debug_mode && power_mode)
|
||||||
|
printf("IRQ delta is %lu, switching to performance mode \n", (unsigned long)(irq - previous) );
|
||||||
|
power_mode = 0;
|
||||||
|
hysteresis = 0;
|
||||||
|
}
|
||||||
|
previous = irq;
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,88 @@
|
||||||
|
/*
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "cpumask.h"
|
||||||
|
#include "irqbalance.h"
|
||||||
|
|
||||||
|
#define LINESIZE 4096
|
||||||
|
|
||||||
|
void parse_proc_interrupts(void)
|
||||||
|
{
|
||||||
|
FILE *file;
|
||||||
|
char line[LINESIZE+1];
|
||||||
|
|
||||||
|
line[LINESIZE] = 0;
|
||||||
|
file = fopen("/proc/interrupts", "r");
|
||||||
|
if (!file)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* first line is the header we don't need; nuke it */
|
||||||
|
if (fgets(line, LINESIZE, file)==NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
while (!feof(file)) {
|
||||||
|
cpumask_t present;
|
||||||
|
int cpunr;
|
||||||
|
int number;
|
||||||
|
uint64_t count;
|
||||||
|
char *c, *c2;
|
||||||
|
|
||||||
|
if (fgets(line, LINESIZE, file)==NULL)
|
||||||
|
break;
|
||||||
|
|
||||||
|
|
||||||
|
/* lines with letters in front are special, like NMI count. Ignore */
|
||||||
|
if (!(line[0]==' ' || (line[0]>='0' && line[0]<='9')))
|
||||||
|
break;
|
||||||
|
c = strchr(line, ':');
|
||||||
|
if (!c)
|
||||||
|
continue;
|
||||||
|
*c = 0;
|
||||||
|
c++;
|
||||||
|
number = strtoul(line, NULL, 10);
|
||||||
|
cpus_clear(present);
|
||||||
|
count = 0;
|
||||||
|
cpunr = 0;
|
||||||
|
|
||||||
|
c2=NULL;
|
||||||
|
while (1) {
|
||||||
|
uint64_t C;
|
||||||
|
C = strtoull(c, &c2, 10);
|
||||||
|
if (c==c2) /* end of numbers */
|
||||||
|
break;
|
||||||
|
count += C;
|
||||||
|
c=c2;
|
||||||
|
if (C)
|
||||||
|
cpu_set(cpunr, present);
|
||||||
|
cpunr++;
|
||||||
|
}
|
||||||
|
if (cpunr != core_count)
|
||||||
|
need_cpu_rescan = 1;
|
||||||
|
|
||||||
|
set_interrupt_count(number, count, &present);
|
||||||
|
}
|
||||||
|
fclose(file);
|
||||||
|
}
|
|
@ -0,0 +1,573 @@
|
||||||
|
execve("./irqbalance", ["./irqbalance", "debug"], [/* 32 vars */]) = 0
|
||||||
|
brk(0) = 0x605000
|
||||||
|
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x2b3a8b1b5000
|
||||||
|
uname({sys="Linux", node="benny", ...}) = 0
|
||||||
|
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
|
||||||
|
open("/etc/ld.so.cache", O_RDONLY) = 3
|
||||||
|
fstat(3, {st_mode=S_IFREG|0644, st_size=171080, ...}) = 0
|
||||||
|
mmap(NULL, 171080, PROT_READ, MAP_PRIVATE, 3, 0) = 0x2b3a8b1b6000
|
||||||
|
close(3) = 0
|
||||||
|
open("/lib64/libglib-2.0.so.0", O_RDONLY) = 3
|
||||||
|
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0`\0!\347"..., 832) = 832
|
||||||
|
fstat(3, {st_mode=S_IFREG|0755, st_size=1839568, ...}) = 0
|
||||||
|
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x2b3a8b1e0000
|
||||||
|
mmap(0x38e7200000, 2743240, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x38e7200000
|
||||||
|
mprotect(0x38e729d000, 2093056, PROT_NONE) = 0
|
||||||
|
mmap(0x38e749c000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x9c000) = 0x38e749c000
|
||||||
|
close(3) = 0
|
||||||
|
open("/lib64/libc.so.6", O_RDONLY) = 3
|
||||||
|
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0@\333\1\0"..., 832) = 832
|
||||||
|
fstat(3, {st_mode=S_IFREG|0755, st_size=1672888, ...}) = 0
|
||||||
|
mmap(NULL, 3461304, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x2b3a8b3b6000
|
||||||
|
mprotect(0x2b3a8b4fa000, 2097152, PROT_NONE) = 0
|
||||||
|
mmap(0x2b3a8b6fa000, 20480, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x144000) = 0x2b3a8b6fa000
|
||||||
|
mmap(0x2b3a8b6ff000, 16568, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x2b3a8b6ff000
|
||||||
|
close(3) = 0
|
||||||
|
open("/lib64/librt.so.1", O_RDONLY) = 3
|
||||||
|
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\300#\0\0"..., 832) = 832
|
||||||
|
fstat(3, {st_mode=S_IFREG|0755, st_size=50320, ...}) = 0
|
||||||
|
mmap(NULL, 2132968, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x2b3a8b704000
|
||||||
|
mprotect(0x2b3a8b70c000, 2093056, PROT_NONE) = 0
|
||||||
|
mmap(0x2b3a8b90b000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x7000) = 0x2b3a8b90b000
|
||||||
|
close(3) = 0
|
||||||
|
open("/lib64/libpthread.so.0", O_RDONLY) = 3
|
||||||
|
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0`X\0\0\0"..., 832) = 832
|
||||||
|
fstat(3, {st_mode=S_IFREG|0755, st_size=138080, ...}) = 0
|
||||||
|
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x2b3a8b90d000
|
||||||
|
mmap(NULL, 2200432, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x2b3a8b90e000
|
||||||
|
mprotect(0x2b3a8b923000, 2093056, PROT_NONE) = 0
|
||||||
|
mmap(0x2b3a8bb22000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x14000) = 0x2b3a8bb22000
|
||||||
|
mmap(0x2b3a8bb24000, 13168, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x2b3a8bb24000
|
||||||
|
close(3) = 0
|
||||||
|
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x2b3a8bb28000
|
||||||
|
arch_prctl(ARCH_SET_FS, 0x2b3a8bb286f0) = 0
|
||||||
|
mprotect(0x2b3a8bb22000, 4096, PROT_READ) = 0
|
||||||
|
mprotect(0x2b3a8b90b000, 4096, PROT_READ) = 0
|
||||||
|
mprotect(0x2b3a8b6fa000, 16384, PROT_READ) = 0
|
||||||
|
mprotect(0x2b3a8b3b4000, 4096, PROT_READ) = 0
|
||||||
|
munmap(0x2b3a8b1b6000, 171080) = 0
|
||||||
|
set_tid_address(0x2b3a8bb28780) = 3829
|
||||||
|
syscall_273(0x2b3a8bb28790, 0x18, 0x7fff1f90e200, 0x2b3a8b19b4a0, 0x2b3a8bb286f0, 0x2b3a8b1b52b8, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2) = 0
|
||||||
|
rt_sigaction(SIGRTMIN, {0x2b3a8b9134a0, [], SA_RESTORER|SA_SIGINFO, 0x2b3a8b91bde0}, NULL, 8) = 0
|
||||||
|
rt_sigaction(SIGRT_1, {0x2b3a8b9133f0, [], SA_RESTORER|SA_RESTART|SA_SIGINFO, 0x2b3a8b91bde0}, NULL, 8) = 0
|
||||||
|
rt_sigprocmask(SIG_UNBLOCK, [RTMIN RT_1], NULL, 8) = 0
|
||||||
|
getrlimit(RLIMIT_STACK, {rlim_cur=8192*1024, rlim_max=RLIM_INFINITY}) = 0
|
||||||
|
open("/sys/devices/system/cpu", O_RDONLY|O_NONBLOCK|O_DIRECTORY) = 3
|
||||||
|
fstat(3, {st_mode=S_IFDIR|0755, st_size=0, ...}) = 0
|
||||||
|
fcntl(3, F_SETFD, FD_CLOEXEC) = 0
|
||||||
|
brk(0) = 0x605000
|
||||||
|
brk(0x627000) = 0x627000
|
||||||
|
getdents(3, /* 11 entries */, 4096) = 288
|
||||||
|
open("/sys/devices/system/cpu/cpu7/online", O_RDONLY) = 4
|
||||||
|
fstat(4, {st_mode=S_IFREG|0600, st_size=4096, ...}) = 0
|
||||||
|
read(4, "1\n", 4096) = 2
|
||||||
|
close(4) = 0
|
||||||
|
open("/sys/devices/system/cpu/cpu7/topology/core_siblings", O_RDONLY) = 4
|
||||||
|
fstat(4, {st_mode=S_IFREG|0444, st_size=4096, ...}) = 0
|
||||||
|
read(4, "00000000,00000000,00000000,00000"..., 4096) = 72
|
||||||
|
close(4) = 0
|
||||||
|
open("/sys/devices/system/cpu/cpu7/cache/index1/shared_cpu_map", O_RDONLY) = 4
|
||||||
|
fstat(4, {st_mode=S_IFREG|0444, st_size=4096, ...}) = 0
|
||||||
|
read(4, "00000000,00000000,00000000,00000"..., 4096) = 72
|
||||||
|
close(4) = 0
|
||||||
|
open("/sys/devices/system/cpu/cpu7/cache/index2/shared_cpu_map", O_RDONLY) = 4
|
||||||
|
fstat(4, {st_mode=S_IFREG|0444, st_size=4096, ...}) = 0
|
||||||
|
read(4, "00000000,00000000,00000000,00000"..., 4096) = 72
|
||||||
|
close(4) = 0
|
||||||
|
open("/sys/devices/system/cpu/cpu6/online", O_RDONLY) = 4
|
||||||
|
fstat(4, {st_mode=S_IFREG|0600, st_size=4096, ...}) = 0
|
||||||
|
read(4, "1\n", 4096) = 2
|
||||||
|
close(4) = 0
|
||||||
|
open("/sys/devices/system/cpu/cpu6/topology/core_siblings", O_RDONLY) = 4
|
||||||
|
fstat(4, {st_mode=S_IFREG|0444, st_size=4096, ...}) = 0
|
||||||
|
read(4, "00000000,00000000,00000000,00000"..., 4096) = 72
|
||||||
|
close(4) = 0
|
||||||
|
open("/sys/devices/system/cpu/cpu6/cache/index1/shared_cpu_map", O_RDONLY) = 4
|
||||||
|
fstat(4, {st_mode=S_IFREG|0444, st_size=4096, ...}) = 0
|
||||||
|
read(4, "00000000,00000000,00000000,00000"..., 4096) = 72
|
||||||
|
close(4) = 0
|
||||||
|
open("/sys/devices/system/cpu/cpu6/cache/index2/shared_cpu_map", O_RDONLY) = 4
|
||||||
|
fstat(4, {st_mode=S_IFREG|0444, st_size=4096, ...}) = 0
|
||||||
|
read(4, "00000000,00000000,00000000,00000"..., 4096) = 72
|
||||||
|
close(4) = 0
|
||||||
|
open("/sys/devices/system/cpu/cpu5/online", O_RDONLY) = 4
|
||||||
|
fstat(4, {st_mode=S_IFREG|0600, st_size=4096, ...}) = 0
|
||||||
|
read(4, "1\n", 4096) = 2
|
||||||
|
close(4) = 0
|
||||||
|
open("/sys/devices/system/cpu/cpu5/topology/core_siblings", O_RDONLY) = 4
|
||||||
|
fstat(4, {st_mode=S_IFREG|0444, st_size=4096, ...}) = 0
|
||||||
|
read(4, "00000000,00000000,00000000,00000"..., 4096) = 72
|
||||||
|
close(4) = 0
|
||||||
|
open("/sys/devices/system/cpu/cpu5/cache/index1/shared_cpu_map", O_RDONLY) = 4
|
||||||
|
fstat(4, {st_mode=S_IFREG|0444, st_size=4096, ...}) = 0
|
||||||
|
read(4, "00000000,00000000,00000000,00000"..., 4096) = 72
|
||||||
|
close(4) = 0
|
||||||
|
open("/sys/devices/system/cpu/cpu5/cache/index2/shared_cpu_map", O_RDONLY) = 4
|
||||||
|
fstat(4, {st_mode=S_IFREG|0444, st_size=4096, ...}) = 0
|
||||||
|
read(4, "00000000,00000000,00000000,00000"..., 4096) = 72
|
||||||
|
close(4) = 0
|
||||||
|
open("/sys/devices/system/cpu/cpu4/online", O_RDONLY) = 4
|
||||||
|
fstat(4, {st_mode=S_IFREG|0600, st_size=4096, ...}) = 0
|
||||||
|
read(4, "1\n", 4096) = 2
|
||||||
|
close(4) = 0
|
||||||
|
open("/sys/devices/system/cpu/cpu4/topology/core_siblings", O_RDONLY) = 4
|
||||||
|
fstat(4, {st_mode=S_IFREG|0444, st_size=4096, ...}) = 0
|
||||||
|
read(4, "00000000,00000000,00000000,00000"..., 4096) = 72
|
||||||
|
close(4) = 0
|
||||||
|
open("/sys/devices/system/cpu/cpu4/cache/index1/shared_cpu_map", O_RDONLY) = 4
|
||||||
|
fstat(4, {st_mode=S_IFREG|0444, st_size=4096, ...}) = 0
|
||||||
|
read(4, "00000000,00000000,00000000,00000"..., 4096) = 72
|
||||||
|
close(4) = 0
|
||||||
|
open("/sys/devices/system/cpu/cpu4/cache/index2/shared_cpu_map", O_RDONLY) = 4
|
||||||
|
fstat(4, {st_mode=S_IFREG|0444, st_size=4096, ...}) = 0
|
||||||
|
read(4, "00000000,00000000,00000000,00000"..., 4096) = 72
|
||||||
|
close(4) = 0
|
||||||
|
open("/sys/devices/system/cpu/cpu3/online", O_RDONLY) = 4
|
||||||
|
fstat(4, {st_mode=S_IFREG|0600, st_size=4096, ...}) = 0
|
||||||
|
read(4, "1\n", 4096) = 2
|
||||||
|
close(4) = 0
|
||||||
|
open("/sys/devices/system/cpu/cpu3/topology/core_siblings", O_RDONLY) = 4
|
||||||
|
fstat(4, {st_mode=S_IFREG|0444, st_size=4096, ...}) = 0
|
||||||
|
read(4, "00000000,00000000,00000000,00000"..., 4096) = 72
|
||||||
|
close(4) = 0
|
||||||
|
open("/sys/devices/system/cpu/cpu3/cache/index1/shared_cpu_map", O_RDONLY) = 4
|
||||||
|
fstat(4, {st_mode=S_IFREG|0444, st_size=4096, ...}) = 0
|
||||||
|
read(4, "00000000,00000000,00000000,00000"..., 4096) = 72
|
||||||
|
close(4) = 0
|
||||||
|
open("/sys/devices/system/cpu/cpu3/cache/index2/shared_cpu_map", O_RDONLY) = 4
|
||||||
|
fstat(4, {st_mode=S_IFREG|0444, st_size=4096, ...}) = 0
|
||||||
|
read(4, "00000000,00000000,00000000,00000"..., 4096) = 72
|
||||||
|
close(4) = 0
|
||||||
|
open("/sys/devices/system/cpu/cpu2/online", O_RDONLY) = 4
|
||||||
|
fstat(4, {st_mode=S_IFREG|0600, st_size=4096, ...}) = 0
|
||||||
|
read(4, "1\n", 4096) = 2
|
||||||
|
close(4) = 0
|
||||||
|
open("/sys/devices/system/cpu/cpu2/topology/core_siblings", O_RDONLY) = 4
|
||||||
|
fstat(4, {st_mode=S_IFREG|0444, st_size=4096, ...}) = 0
|
||||||
|
read(4, "00000000,00000000,00000000,00000"..., 4096) = 72
|
||||||
|
close(4) = 0
|
||||||
|
open("/sys/devices/system/cpu/cpu2/cache/index1/shared_cpu_map", O_RDONLY) = 4
|
||||||
|
fstat(4, {st_mode=S_IFREG|0444, st_size=4096, ...}) = 0
|
||||||
|
read(4, "00000000,00000000,00000000,00000"..., 4096) = 72
|
||||||
|
close(4) = 0
|
||||||
|
open("/sys/devices/system/cpu/cpu2/cache/index2/shared_cpu_map", O_RDONLY) = 4
|
||||||
|
fstat(4, {st_mode=S_IFREG|0444, st_size=4096, ...}) = 0
|
||||||
|
read(4, "00000000,00000000,00000000,00000"..., 4096) = 72
|
||||||
|
close(4) = 0
|
||||||
|
open("/sys/devices/system/cpu/cpu1/online", O_RDONLY) = 4
|
||||||
|
fstat(4, {st_mode=S_IFREG|0600, st_size=4096, ...}) = 0
|
||||||
|
read(4, "1\n", 4096) = 2
|
||||||
|
close(4) = 0
|
||||||
|
open("/sys/devices/system/cpu/cpu1/topology/core_siblings", O_RDONLY) = 4
|
||||||
|
fstat(4, {st_mode=S_IFREG|0444, st_size=4096, ...}) = 0
|
||||||
|
read(4, "00000000,00000000,00000000,00000"..., 4096) = 72
|
||||||
|
close(4) = 0
|
||||||
|
open("/sys/devices/system/cpu/cpu1/cache/index1/shared_cpu_map", O_RDONLY) = 4
|
||||||
|
fstat(4, {st_mode=S_IFREG|0444, st_size=4096, ...}) = 0
|
||||||
|
read(4, "00000000,00000000,00000000,00000"..., 4096) = 72
|
||||||
|
close(4) = 0
|
||||||
|
open("/sys/devices/system/cpu/cpu1/cache/index2/shared_cpu_map", O_RDONLY) = 4
|
||||||
|
fstat(4, {st_mode=S_IFREG|0444, st_size=4096, ...}) = 0
|
||||||
|
read(4, "00000000,00000000,00000000,00000"..., 4096) = 72
|
||||||
|
close(4) = 0
|
||||||
|
open("/sys/devices/system/cpu/cpu0/online", O_RDONLY) = -1 ENOENT (No such file or directory)
|
||||||
|
open("/sys/devices/system/cpu/cpu0/topology/core_siblings", O_RDONLY) = 4
|
||||||
|
fstat(4, {st_mode=S_IFREG|0444, st_size=4096, ...}) = 0
|
||||||
|
read(4, "00000000,00000000,00000000,00000"..., 4096) = 72
|
||||||
|
close(4) = 0
|
||||||
|
open("/sys/devices/system/cpu/cpu0/cache/index1/shared_cpu_map", O_RDONLY) = 4
|
||||||
|
fstat(4, {st_mode=S_IFREG|0444, st_size=4096, ...}) = 0
|
||||||
|
read(4, "00000000,00000000,00000000,00000"..., 4096) = 72
|
||||||
|
close(4) = 0
|
||||||
|
open("/sys/devices/system/cpu/cpu0/cache/index2/shared_cpu_map", O_RDONLY) = 4
|
||||||
|
fstat(4, {st_mode=S_IFREG|0444, st_size=4096, ...}) = 0
|
||||||
|
read(4, "00000000,00000000,00000000,00000"..., 4096) = 72
|
||||||
|
close(4) = 0
|
||||||
|
getdents(3, /* 0 entries */, 4096) = 0
|
||||||
|
close(3) = 0
|
||||||
|
fstat(1, {st_mode=S_IFREG|0644, st_size=11425, ...}) = 0
|
||||||
|
open("/proc/interrupts", O_RDONLY) = 3
|
||||||
|
fstat(3, {st_mode=S_IFREG|0444, st_size=0, ...}) = 0
|
||||||
|
read(3, " CPU0 CPU1 "..., 1024) = 1024
|
||||||
|
open("/proc/irq/0", O_RDONLY|O_NONBLOCK|O_DIRECTORY) = 4
|
||||||
|
fstat(4, {st_mode=S_IFDIR|0555, st_size=0, ...}) = 0
|
||||||
|
fcntl(4, F_SETFD, FD_CLOEXEC) = 0
|
||||||
|
getdents(4, /* 3 entries */, 1024) = 80
|
||||||
|
open("/proc/irq/0/smp_affinity", O_RDONLY) = 5
|
||||||
|
fstat(5, {st_mode=S_IFREG|0600, st_size=0, ...}) = 0
|
||||||
|
read(5, "00000000,00000000,00000000,00000"..., 1024) = 72
|
||||||
|
close(5) = 0
|
||||||
|
getdents(4, /* 0 entries */, 1024) = 0
|
||||||
|
close(4) = 0
|
||||||
|
read(3, " 0 IO-APIC-edge i8042\n 1"..., 1024) = 1024
|
||||||
|
open("/proc/irq/12", O_RDONLY|O_NONBLOCK|O_DIRECTORY) = 4
|
||||||
|
fstat(4, {st_mode=S_IFDIR|0555, st_size=0, ...}) = 0
|
||||||
|
fcntl(4, F_SETFD, FD_CLOEXEC) = 0
|
||||||
|
getdents(4, /* 4 entries */, 1024) = 112
|
||||||
|
open("/proc/irq/12/smp_affinity", O_RDONLY) = 5
|
||||||
|
fstat(5, {st_mode=S_IFREG|0600, st_size=0, ...}) = 0
|
||||||
|
read(5, "00000000,00000000,00000000,00000"..., 1024) = 72
|
||||||
|
close(5) = 0
|
||||||
|
getdents(4, /* 0 entries */, 1024) = 0
|
||||||
|
close(4) = 0
|
||||||
|
open("/proc/irq/14", O_RDONLY|O_NONBLOCK|O_DIRECTORY) = 4
|
||||||
|
fstat(4, {st_mode=S_IFDIR|0555, st_size=0, ...}) = 0
|
||||||
|
fcntl(4, F_SETFD, FD_CLOEXEC) = 0
|
||||||
|
getdents(4, /* 4 entries */, 1024) = 104
|
||||||
|
open("/proc/irq/14/smp_affinity", O_RDONLY) = 5
|
||||||
|
fstat(5, {st_mode=S_IFREG|0600, st_size=0, ...}) = 0
|
||||||
|
read(5, "00000000,00000000,00000000,00000"..., 1024) = 72
|
||||||
|
close(5) = 0
|
||||||
|
getdents(4, /* 0 entries */, 1024) = 0
|
||||||
|
close(4) = 0
|
||||||
|
open("/proc/irq/16", O_RDONLY|O_NONBLOCK|O_DIRECTORY) = 4
|
||||||
|
fstat(4, {st_mode=S_IFDIR|0555, st_size=0, ...}) = 0
|
||||||
|
fcntl(4, F_SETFD, FD_CLOEXEC) = 0
|
||||||
|
getdents(4, /* 4 entries */, 1024) = 112
|
||||||
|
open("/proc/irq/16/smp_affinity", O_RDONLY) = 5
|
||||||
|
fstat(5, {st_mode=S_IFREG|0600, st_size=0, ...}) = 0
|
||||||
|
read(5, "00000000,00000000,00000000,00000"..., 1024) = 72
|
||||||
|
close(5) = 0
|
||||||
|
getdents(4, /* 0 entries */, 1024) = 0
|
||||||
|
close(4) = 0
|
||||||
|
open("/proc/irq/19", O_RDONLY|O_NONBLOCK|O_DIRECTORY) = 4
|
||||||
|
fstat(4, {st_mode=S_IFDIR|0555, st_size=0, ...}) = 0
|
||||||
|
fcntl(4, F_SETFD, FD_CLOEXEC) = 0
|
||||||
|
getdents(4, /* 5 entries */, 1024) = 152
|
||||||
|
open("/proc/irq/19/smp_affinity", O_RDONLY) = 5
|
||||||
|
fstat(5, {st_mode=S_IFREG|0600, st_size=0, ...}) = 0
|
||||||
|
read(5, "00000000,00000000,00000000,00000"..., 1024) = 72
|
||||||
|
close(5) = 0
|
||||||
|
getdents(4, /* 0 entries */, 1024) = 0
|
||||||
|
close(4) = 0
|
||||||
|
open("/proc/irq/8408", O_RDONLY|O_NONBLOCK|O_DIRECTORY) = 4
|
||||||
|
fstat(4, {st_mode=S_IFDIR|0555, st_size=0, ...}) = 0
|
||||||
|
fcntl(4, F_SETFD, FD_CLOEXEC) = 0
|
||||||
|
getdents(4, /* 4 entries */, 1024) = 104
|
||||||
|
open("/proc/irq/8408/smp_affinity", O_RDONLY) = 5
|
||||||
|
fstat(5, {st_mode=S_IFREG|0600, st_size=0, ...}) = 0
|
||||||
|
read(5, "00000000,00000000,00000000,00000"..., 1024) = 72
|
||||||
|
close(5) = 0
|
||||||
|
getdents(4, /* 0 entries */, 1024) = 0
|
||||||
|
close(4) = 0
|
||||||
|
open("/proc/irq/8409", O_RDONLY|O_NONBLOCK|O_DIRECTORY) = 4
|
||||||
|
fstat(4, {st_mode=S_IFDIR|0555, st_size=0, ...}) = 0
|
||||||
|
fcntl(4, F_SETFD, FD_CLOEXEC) = 0
|
||||||
|
getdents(4, /* 4 entries */, 1024) = 104
|
||||||
|
open("/proc/irq/8409/smp_affinity", O_RDONLY) = 5
|
||||||
|
fstat(5, {st_mode=S_IFREG|0600, st_size=0, ...}) = 0
|
||||||
|
read(5, "00000000,00000000,00000000,00000"..., 1024) = 72
|
||||||
|
close(5) = 0
|
||||||
|
getdents(4, /* 0 entries */, 1024) = 0
|
||||||
|
close(4) = 0
|
||||||
|
close(3) = 0
|
||||||
|
rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
|
||||||
|
rt_sigaction(SIGCHLD, NULL, {SIG_DFL}, 8) = 0
|
||||||
|
rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
|
||||||
|
nanosleep({2, 0}, {2, 0}) = 0
|
||||||
|
open("/proc/interrupts", O_RDONLY) = 3
|
||||||
|
fstat(3, {st_mode=S_IFREG|0444, st_size=0, ...}) = 0
|
||||||
|
read(3, " CPU0 CPU1 "..., 1024) = 1024
|
||||||
|
read(3, " 0 IO-APIC-edge i8042\n 1"..., 1024) = 1024
|
||||||
|
close(3) = 0
|
||||||
|
open("/sys/bus/pci/devices", O_RDONLY|O_NONBLOCK|O_DIRECTORY) = 3
|
||||||
|
fstat(3, {st_mode=S_IFDIR|0755, st_size=0, ...}) = 0
|
||||||
|
fcntl(3, F_SETFD, FD_CLOEXEC) = 0
|
||||||
|
getdents(3, /* 35 entries */, 4096) = 1104
|
||||||
|
open("/sys/bus/pci/devices/0000:0b:01.0/irq", O_RDONLY) = 4
|
||||||
|
fstat(4, {st_mode=S_IFREG|0444, st_size=4096, ...}) = 0
|
||||||
|
read(4, "11\n", 4096) = 3
|
||||||
|
close(4) = 0
|
||||||
|
open("/sys/bus/pci/devices/0000:0b:01.0/class", O_RDONLY) = 4
|
||||||
|
fstat(4, {st_mode=S_IFREG|0444, st_size=4096, ...}) = 0
|
||||||
|
read(4, "0x030000\n", 4096) = 9
|
||||||
|
close(4) = 0
|
||||||
|
open("/sys/bus/pci/devices/0000:0b:01.0/local_cpus", O_RDONLY) = 4
|
||||||
|
fstat(4, {st_mode=S_IFREG|0444, st_size=4096, ...}) = 0
|
||||||
|
read(4, "00000000,00000000,00000000,00000"..., 4096) = 72
|
||||||
|
close(4) = 0
|
||||||
|
open("/sys/bus/pci/devices/0000:06:00.1/irq", O_RDONLY) = 4
|
||||||
|
fstat(4, {st_mode=S_IFREG|0444, st_size=4096, ...}) = 0
|
||||||
|
read(4, "8408\n", 4096) = 5
|
||||||
|
close(4) = 0
|
||||||
|
open("/sys/bus/pci/devices/0000:06:00.1/class", O_RDONLY) = 4
|
||||||
|
fstat(4, {st_mode=S_IFREG|0444, st_size=4096, ...}) = 0
|
||||||
|
read(4, "0x020000\n", 4096) = 9
|
||||||
|
close(4) = 0
|
||||||
|
open("/sys/bus/pci/devices/0000:06:00.1/local_cpus", O_RDONLY) = 4
|
||||||
|
fstat(4, {st_mode=S_IFREG|0444, st_size=4096, ...}) = 0
|
||||||
|
read(4, "00000000,00000000,00000000,00000"..., 4096) = 72
|
||||||
|
close(4) = 0
|
||||||
|
open("/sys/bus/pci/devices/0000:06:00.0/irq", O_RDONLY) = 4
|
||||||
|
fstat(4, {st_mode=S_IFREG|0444, st_size=4096, ...}) = 0
|
||||||
|
read(4, "8409\n", 4096) = 5
|
||||||
|
close(4) = 0
|
||||||
|
open("/sys/bus/pci/devices/0000:06:00.0/class", O_RDONLY) = 4
|
||||||
|
fstat(4, {st_mode=S_IFREG|0444, st_size=4096, ...}) = 0
|
||||||
|
read(4, "0x020000\n", 4096) = 9
|
||||||
|
close(4) = 0
|
||||||
|
open("/sys/bus/pci/devices/0000:06:00.0/local_cpus", O_RDONLY) = 4
|
||||||
|
fstat(4, {st_mode=S_IFREG|0444, st_size=4096, ...}) = 0
|
||||||
|
read(4, "00000000,00000000,00000000,00000"..., 4096) = 72
|
||||||
|
close(4) = 0
|
||||||
|
open("/sys/bus/pci/devices/0000:04:02.1/irq", O_RDONLY) = 4
|
||||||
|
fstat(4, {st_mode=S_IFREG|0444, st_size=4096, ...}) = 0
|
||||||
|
read(4, "17\n", 4096) = 3
|
||||||
|
close(4) = 0
|
||||||
|
open("/sys/bus/pci/devices/0000:04:02.1/class", O_RDONLY) = 4
|
||||||
|
fstat(4, {st_mode=S_IFREG|0444, st_size=4096, ...}) = 0
|
||||||
|
read(4, "0x010000\n", 4096) = 9
|
||||||
|
close(4) = 0
|
||||||
|
open("/sys/bus/pci/devices/0000:04:02.1/local_cpus", O_RDONLY) = 4
|
||||||
|
fstat(4, {st_mode=S_IFREG|0444, st_size=4096, ...}) = 0
|
||||||
|
read(4, "00000000,00000000,00000000,00000"..., 4096) = 72
|
||||||
|
close(4) = 0
|
||||||
|
open("/sys/bus/pci/devices/0000:04:02.0/irq", O_RDONLY) = 4
|
||||||
|
fstat(4, {st_mode=S_IFREG|0444, st_size=4096, ...}) = 0
|
||||||
|
read(4, "16\n", 4096) = 3
|
||||||
|
close(4) = 0
|
||||||
|
open("/sys/bus/pci/devices/0000:04:02.0/class", O_RDONLY) = 4
|
||||||
|
fstat(4, {st_mode=S_IFREG|0444, st_size=4096, ...}) = 0
|
||||||
|
read(4, "0x010000\n", 4096) = 9
|
||||||
|
close(4) = 0
|
||||||
|
open("/sys/bus/pci/devices/0000:04:02.0/local_cpus", O_RDONLY) = 4
|
||||||
|
fstat(4, {st_mode=S_IFREG|0444, st_size=4096, ...}) = 0
|
||||||
|
read(4, "00000000,00000000,00000000,00000"..., 4096) = 72
|
||||||
|
close(4) = 0
|
||||||
|
open("/sys/bus/pci/devices/0000:03:00.2/irq", O_RDONLY) = 4
|
||||||
|
fstat(4, {st_mode=S_IFREG|0444, st_size=4096, ...}) = 0
|
||||||
|
read(4, "0\n", 4096) = 2
|
||||||
|
close(4) = 0
|
||||||
|
open("/sys/bus/pci/devices/0000:03:00.0/irq", O_RDONLY) = 4
|
||||||
|
fstat(4, {st_mode=S_IFREG|0444, st_size=4096, ...}) = 0
|
||||||
|
read(4, "0\n", 4096) = 2
|
||||||
|
close(4) = 0
|
||||||
|
open("/sys/bus/pci/devices/0000:02:02.0/irq", O_RDONLY) = 4
|
||||||
|
fstat(4, {st_mode=S_IFREG|0444, st_size=4096, ...}) = 0
|
||||||
|
read(4, "8410\n", 4096) = 5
|
||||||
|
close(4) = 0
|
||||||
|
open("/sys/bus/pci/devices/0000:02:02.0/class", O_RDONLY) = 4
|
||||||
|
fstat(4, {st_mode=S_IFREG|0444, st_size=4096, ...}) = 0
|
||||||
|
read(4, "0x060400\n", 4096) = 9
|
||||||
|
close(4) = 0
|
||||||
|
open("/sys/bus/pci/devices/0000:02:02.0/local_cpus", O_RDONLY) = 4
|
||||||
|
fstat(4, {st_mode=S_IFREG|0444, st_size=4096, ...}) = 0
|
||||||
|
read(4, "00000000,00000000,00000000,00000"..., 4096) = 72
|
||||||
|
close(4) = 0
|
||||||
|
open("/sys/bus/pci/devices/0000:02:00.0/irq", O_RDONLY) = 4
|
||||||
|
fstat(4, {st_mode=S_IFREG|0444, st_size=4096, ...}) = 0
|
||||||
|
read(4, "8411\n", 4096) = 5
|
||||||
|
close(4) = 0
|
||||||
|
open("/sys/bus/pci/devices/0000:02:00.0/class", O_RDONLY) = 4
|
||||||
|
fstat(4, {st_mode=S_IFREG|0444, st_size=4096, ...}) = 0
|
||||||
|
read(4, "0x060400\n", 4096) = 9
|
||||||
|
close(4) = 0
|
||||||
|
open("/sys/bus/pci/devices/0000:02:00.0/local_cpus", O_RDONLY) = 4
|
||||||
|
fstat(4, {st_mode=S_IFREG|0444, st_size=4096, ...}) = 0
|
||||||
|
read(4, "00000000,00000000,00000000,00000"..., 4096) = 72
|
||||||
|
close(4) = 0
|
||||||
|
open("/sys/bus/pci/devices/0000:01:00.3/irq", O_RDONLY) = 4
|
||||||
|
fstat(4, {st_mode=S_IFREG|0444, st_size=4096, ...}) = 0
|
||||||
|
read(4, "0\n", 4096) = 2
|
||||||
|
close(4) = 0
|
||||||
|
open("/sys/bus/pci/devices/0000:01:00.0/irq", O_RDONLY) = 4
|
||||||
|
fstat(4, {st_mode=S_IFREG|0444, st_size=4096, ...}) = 0
|
||||||
|
read(4, "16\n", 4096) = 3
|
||||||
|
close(4) = 0
|
||||||
|
open("/sys/bus/pci/devices/0000:01:00.0/class", O_RDONLY) = 4
|
||||||
|
fstat(4, {st_mode=S_IFREG|0444, st_size=4096, ...}) = 0
|
||||||
|
read(4, "0x060400\n", 4096) = 9
|
||||||
|
close(4) = 0
|
||||||
|
open("/sys/bus/pci/devices/0000:01:00.0/local_cpus", O_RDONLY) = 4
|
||||||
|
fstat(4, {st_mode=S_IFREG|0444, st_size=4096, ...}) = 0
|
||||||
|
read(4, "00000000,00000000,00000000,00000"..., 4096) = 72
|
||||||
|
close(4) = 0
|
||||||
|
open("/sys/bus/pci/devices/0000:00:1f.3/irq", O_RDONLY) = 4
|
||||||
|
fstat(4, {st_mode=S_IFREG|0444, st_size=4096, ...}) = 0
|
||||||
|
read(4, "19\n", 4096) = 3
|
||||||
|
close(4) = 0
|
||||||
|
open("/sys/bus/pci/devices/0000:00:1f.3/class", O_RDONLY) = 4
|
||||||
|
fstat(4, {st_mode=S_IFREG|0444, st_size=4096, ...}) = 0
|
||||||
|
read(4, "0x0c0500\n", 4096) = 9
|
||||||
|
close(4) = 0
|
||||||
|
open("/sys/bus/pci/devices/0000:00:1f.3/local_cpus", O_RDONLY) = 4
|
||||||
|
fstat(4, {st_mode=S_IFREG|0444, st_size=4096, ...}) = 0
|
||||||
|
read(4, "00000000,00000000,00000000,00000"..., 4096) = 72
|
||||||
|
close(4) = 0
|
||||||
|
open("/sys/bus/pci/devices/0000:00:1f.2/irq", O_RDONLY) = 4
|
||||||
|
fstat(4, {st_mode=S_IFREG|0444, st_size=4096, ...}) = 0
|
||||||
|
read(4, "19\n", 4096) = 3
|
||||||
|
close(4) = 0
|
||||||
|
open("/sys/bus/pci/devices/0000:00:1f.2/class", O_RDONLY) = 4
|
||||||
|
fstat(4, {st_mode=S_IFREG|0444, st_size=4096, ...}) = 0
|
||||||
|
read(4, "0x010601\n", 4096) = 9
|
||||||
|
close(4) = 0
|
||||||
|
open("/sys/bus/pci/devices/0000:00:1f.2/local_cpus", O_RDONLY) = 4
|
||||||
|
fstat(4, {st_mode=S_IFREG|0444, st_size=4096, ...}) = 0
|
||||||
|
read(4, "00000000,00000000,00000000,00000"..., 4096) = 72
|
||||||
|
close(4) = 0
|
||||||
|
open("/sys/bus/pci/devices/0000:00:1f.1/irq", O_RDONLY) = 4
|
||||||
|
fstat(4, {st_mode=S_IFREG|0444, st_size=4096, ...}) = 0
|
||||||
|
read(4, "18\n", 4096) = 3
|
||||||
|
close(4) = 0
|
||||||
|
open("/sys/bus/pci/devices/0000:00:1f.1/class", O_RDONLY) = 4
|
||||||
|
fstat(4, {st_mode=S_IFREG|0444, st_size=4096, ...}) = 0
|
||||||
|
read(4, "0x01018a\n", 4096) = 9
|
||||||
|
close(4) = 0
|
||||||
|
open("/sys/bus/pci/devices/0000:00:1f.1/local_cpus", O_RDONLY) = 4
|
||||||
|
fstat(4, {st_mode=S_IFREG|0444, st_size=4096, ...}) = 0
|
||||||
|
read(4, "00000000,00000000,00000000,00000"..., 4096) = 72
|
||||||
|
close(4) = 0
|
||||||
|
open("/sys/bus/pci/devices/0000:00:1f.0/irq", O_RDONLY) = 4
|
||||||
|
fstat(4, {st_mode=S_IFREG|0444, st_size=4096, ...}) = 0
|
||||||
|
read(4, "0\n", 4096) = 2
|
||||||
|
close(4) = 0
|
||||||
|
open("/sys/bus/pci/devices/0000:00:1e.0/irq", O_RDONLY) = 4
|
||||||
|
fstat(4, {st_mode=S_IFREG|0444, st_size=4096, ...}) = 0
|
||||||
|
read(4, "0\n", 4096) = 2
|
||||||
|
close(4) = 0
|
||||||
|
open("/sys/bus/pci/devices/0000:00:1d.7/irq", O_RDONLY) = 4
|
||||||
|
fstat(4, {st_mode=S_IFREG|0444, st_size=4096, ...}) = 0
|
||||||
|
read(4, "17\n", 4096) = 3
|
||||||
|
close(4) = 0
|
||||||
|
open("/sys/bus/pci/devices/0000:00:1d.7/class", O_RDONLY) = 4
|
||||||
|
fstat(4, {st_mode=S_IFREG|0444, st_size=4096, ...}) = 0
|
||||||
|
read(4, "0x0c0320\n", 4096) = 9
|
||||||
|
close(4) = 0
|
||||||
|
open("/sys/bus/pci/devices/0000:00:1d.7/local_cpus", O_RDONLY) = 4
|
||||||
|
fstat(4, {st_mode=S_IFREG|0444, st_size=4096, ...}) = 0
|
||||||
|
read(4, "00000000,00000000,00000000,00000"..., 4096) = 72
|
||||||
|
close(4) = 0
|
||||||
|
open("/sys/bus/pci/devices/0000:00:1d.2/irq", O_RDONLY) = 4
|
||||||
|
fstat(4, {st_mode=S_IFREG|0444, st_size=4096, ...}) = 0
|
||||||
|
read(4, "18\n", 4096) = 3
|
||||||
|
close(4) = 0
|
||||||
|
open("/sys/bus/pci/devices/0000:00:1d.2/class", O_RDONLY) = 4
|
||||||
|
fstat(4, {st_mode=S_IFREG|0444, st_size=4096, ...}) = 0
|
||||||
|
read(4, "0x0c0300\n", 4096) = 9
|
||||||
|
close(4) = 0
|
||||||
|
open("/sys/bus/pci/devices/0000:00:1d.2/local_cpus", O_RDONLY) = 4
|
||||||
|
fstat(4, {st_mode=S_IFREG|0444, st_size=4096, ...}) = 0
|
||||||
|
read(4, "00000000,00000000,00000000,00000"..., 4096) = 72
|
||||||
|
close(4) = 0
|
||||||
|
open("/sys/bus/pci/devices/0000:00:1d.1/irq", O_RDONLY) = 4
|
||||||
|
fstat(4, {st_mode=S_IFREG|0444, st_size=4096, ...}) = 0
|
||||||
|
read(4, "19\n", 4096) = 3
|
||||||
|
close(4) = 0
|
||||||
|
open("/sys/bus/pci/devices/0000:00:1d.1/class", O_RDONLY) = 4
|
||||||
|
fstat(4, {st_mode=S_IFREG|0444, st_size=4096, ...}) = 0
|
||||||
|
read(4, "0x0c0300\n", 4096) = 9
|
||||||
|
close(4) = 0
|
||||||
|
open("/sys/bus/pci/devices/0000:00:1d.1/local_cpus", O_RDONLY) = 4
|
||||||
|
fstat(4, {st_mode=S_IFREG|0444, st_size=4096, ...}) = 0
|
||||||
|
read(4, "00000000,00000000,00000000,00000"..., 4096) = 72
|
||||||
|
close(4) = 0
|
||||||
|
open("/sys/bus/pci/devices/0000:00:1d.0/irq", O_RDONLY) = 4
|
||||||
|
fstat(4, {st_mode=S_IFREG|0444, st_size=4096, ...}) = 0
|
||||||
|
read(4, "17\n", 4096) = 3
|
||||||
|
close(4) = 0
|
||||||
|
open("/sys/bus/pci/devices/0000:00:1d.0/class", O_RDONLY) = 4
|
||||||
|
fstat(4, {st_mode=S_IFREG|0444, st_size=4096, ...}) = 0
|
||||||
|
read(4, "0x0c0300\n", 4096) = 9
|
||||||
|
close(4) = 0
|
||||||
|
open("/sys/bus/pci/devices/0000:00:1d.0/local_cpus", O_RDONLY) = 4
|
||||||
|
fstat(4, {st_mode=S_IFREG|0444, st_size=4096, ...}) = 0
|
||||||
|
read(4, "00000000,00000000,00000000,00000"..., 4096) = 72
|
||||||
|
close(4) = 0
|
||||||
|
open("/sys/bus/pci/devices/0000:00:1c.0/irq", O_RDONLY) = 4
|
||||||
|
fstat(4, {st_mode=S_IFREG|0444, st_size=4096, ...}) = 0
|
||||||
|
read(4, "8412\n", 4096) = 5
|
||||||
|
close(4) = 0
|
||||||
|
open("/sys/bus/pci/devices/0000:00:1c.0/class", O_RDONLY) = 4
|
||||||
|
fstat(4, {st_mode=S_IFREG|0444, st_size=4096, ...}) = 0
|
||||||
|
read(4, "0x060400\n", 4096) = 9
|
||||||
|
close(4) = 0
|
||||||
|
open("/sys/bus/pci/devices/0000:00:1c.0/local_cpus", O_RDONLY) = 4
|
||||||
|
fstat(4, {st_mode=S_IFREG|0444, st_size=4096, ...}) = 0
|
||||||
|
read(4, "00000000,00000000,00000000,00000"..., 4096) = 72
|
||||||
|
close(4) = 0
|
||||||
|
open("/sys/bus/pci/devices/0000:00:16.0/irq", O_RDONLY) = 4
|
||||||
|
fstat(4, {st_mode=S_IFREG|0444, st_size=4096, ...}) = 0
|
||||||
|
read(4, "0\n", 4096) = 2
|
||||||
|
close(4) = 0
|
||||||
|
open("/sys/bus/pci/devices/0000:00:15.0/irq", O_RDONLY) = 4
|
||||||
|
fstat(4, {st_mode=S_IFREG|0444, st_size=4096, ...}) = 0
|
||||||
|
read(4, "0\n", 4096) = 2
|
||||||
|
close(4) = 0
|
||||||
|
open("/sys/bus/pci/devices/0000:00:13.0/irq", O_RDONLY) = 4
|
||||||
|
fstat(4, {st_mode=S_IFREG|0444, st_size=4096, ...}) = 0
|
||||||
|
read(4, "0\n", 4096) = 2
|
||||||
|
close(4) = 0
|
||||||
|
open("/sys/bus/pci/devices/0000:00:11.0/irq", O_RDONLY) = 4
|
||||||
|
fstat(4, {st_mode=S_IFREG|0444, st_size=4096, ...}) = 0
|
||||||
|
read(4, "0\n", 4096) = 2
|
||||||
|
close(4) = 0
|
||||||
|
open("/sys/bus/pci/devices/0000:00:10.2/irq", O_RDONLY) = 4
|
||||||
|
fstat(4, {st_mode=S_IFREG|0444, st_size=4096, ...}) = 0
|
||||||
|
read(4, "0\n", 4096) = 2
|
||||||
|
close(4) = 0
|
||||||
|
open("/sys/bus/pci/devices/0000:00:10.1/irq", O_RDONLY) = 4
|
||||||
|
fstat(4, {st_mode=S_IFREG|0444, st_size=4096, ...}) = 0
|
||||||
|
read(4, "0\n", 4096) = 2
|
||||||
|
close(4) = 0
|
||||||
|
open("/sys/bus/pci/devices/0000:00:10.0/irq", O_RDONLY) = 4
|
||||||
|
fstat(4, {st_mode=S_IFREG|0444, st_size=4096, ...}) = 0
|
||||||
|
read(4, "0\n", 4096) = 2
|
||||||
|
close(4) = 0
|
||||||
|
open("/sys/bus/pci/devices/0000:00:08.0/irq", O_RDONLY) = 4
|
||||||
|
fstat(4, {st_mode=S_IFREG|0444, st_size=4096, ...}) = 0
|
||||||
|
read(4, "9\n", 4096) = 2
|
||||||
|
close(4) = 0
|
||||||
|
open("/sys/bus/pci/devices/0000:00:08.0/class", O_RDONLY) = 4
|
||||||
|
fstat(4, {st_mode=S_IFREG|0444, st_size=4096, ...}) = 0
|
||||||
|
read(4, "0x088000\n", 4096) = 9
|
||||||
|
close(4) = 0
|
||||||
|
open("/sys/bus/pci/devices/0000:00:08.0/local_cpus", O_RDONLY) = 4
|
||||||
|
fstat(4, {st_mode=S_IFREG|0444, st_size=4096, ...}) = 0
|
||||||
|
read(4, "00000000,00000000,00000000,00000"..., 4096) = 72
|
||||||
|
close(4) = 0
|
||||||
|
open("/sys/bus/pci/devices/0000:00:06.0/irq", O_RDONLY) = 4
|
||||||
|
fstat(4, {st_mode=S_IFREG|0444, st_size=4096, ...}) = 0
|
||||||
|
read(4, "8413\n", 4096) = 5
|
||||||
|
close(4) = 0
|
||||||
|
open("/sys/bus/pci/devices/0000:00:06.0/class", O_RDONLY) = 4
|
||||||
|
fstat(4, {st_mode=S_IFREG|0444, st_size=4096, ...}) = 0
|
||||||
|
read(4, "0x060400\n", 4096) = 9
|
||||||
|
close(4) = 0
|
||||||
|
open("/sys/bus/pci/devices/0000:00:06.0/local_cpus", O_RDONLY) = 4
|
||||||
|
fstat(4, {st_mode=S_IFREG|0444, st_size=4096, ...}) = 0
|
||||||
|
read(4, "00000000,00000000,00000000,00000"..., 4096) = 72
|
||||||
|
close(4) = 0
|
||||||
|
open("/sys/bus/pci/devices/0000:00:04.0/irq", O_RDONLY) = 4
|
||||||
|
fstat(4, {st_mode=S_IFREG|0444, st_size=4096, ...}) = 0
|
||||||
|
read(4, "8414\n", 4096) = 5
|
||||||
|
close(4) = 0
|
||||||
|
open("/sys/bus/pci/devices/0000:00:04.0/class", O_RDONLY) = 4
|
||||||
|
fstat(4, {st_mode=S_IFREG|0444, st_size=4096, ...}) = 0
|
||||||
|
read(4, "0x060400\n", 4096) = 9
|
||||||
|
close(4) = 0
|
||||||
|
open("/sys/bus/pci/devices/0000:00:04.0/local_cpus", O_RDONLY) = 4
|
||||||
|
fstat(4, {st_mode=S_IFREG|0444, st_size=4096, ...}) = 0
|
||||||
|
read(4, "00000000,00000000,00000000,00000"..., 4096) = 72
|
||||||
|
close(4) = 0
|
||||||
|
open("/sys/bus/pci/devices/0000:00:02.0/irq", O_RDONLY) = 4
|
||||||
|
fstat(4, {st_mode=S_IFREG|0444, st_size=4096, ...}) = 0
|
||||||
|
read(4, "8415\n", 4096) = 5
|
||||||
|
close(4) = 0
|
||||||
|
open("/sys/bus/pci/devices/0000:00:02.0/class", O_RDONLY) = 4
|
||||||
|
fstat(4, {st_mode=S_IFREG|0444, st_size=4096, ...}) = 0
|
||||||
|
read(4, "0x060400\n", 4096) = 9
|
||||||
|
close(4) = 0
|
||||||
|
open("/sys/bus/pci/devices/0000:00:02.0/local_cpus", O_RDONLY) = 4
|
||||||
|
fstat(4, {st_mode=S_IFREG|0444, st_size=4096, ...}) = 0
|
||||||
|
read(4, "00000000,00000000,00000000,00000"..., 4096) = 72
|
||||||
|
close(4) = 0
|
||||||
|
open("/sys/bus/pci/devices/0000:00:00.0/irq", O_RDONLY) = 4
|
||||||
|
fstat(4, {st_mode=S_IFREG|0444, st_size=4096, ...}) = 0
|
||||||
|
read(4, "0\n", 4096) = 2
|
||||||
|
close(4) = 0
|
||||||
|
getdents(3, /* 0 entries */, 4096) = 0
|
||||||
|
rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
|
||||||
|
rt_sigaction(SIGCHLD, NULL, {SIG_DFL}, 8) = 0
|
||||||
|
rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
|
||||||
|
nanosleep({10, 0}, <unfinished ...>
|
|
@ -0,0 +1,84 @@
|
||||||
|
#ifndef _INCLUDE_GUARD_TYPES_H
|
||||||
|
#define _INCLUDE_GUARD_TYPES_H
|
||||||
|
|
||||||
|
#include <glib.h>
|
||||||
|
|
||||||
|
#include "cpumask.h"
|
||||||
|
|
||||||
|
#define BALANCE_NONE 0
|
||||||
|
#define BALANCE_PACKAGE 1
|
||||||
|
#define BALANCE_CACHE 2
|
||||||
|
#define BALANCE_CORE 3
|
||||||
|
|
||||||
|
#define IRQ_OTHER 0
|
||||||
|
#define IRQ_LEGACY 1
|
||||||
|
#define IRQ_SCSI 2
|
||||||
|
#define IRQ_TIMER 3
|
||||||
|
#define IRQ_ETH 4
|
||||||
|
|
||||||
|
|
||||||
|
struct package {
|
||||||
|
uint64_t workload;
|
||||||
|
int number;
|
||||||
|
|
||||||
|
cpumask_t mask;
|
||||||
|
|
||||||
|
int class_count[7];
|
||||||
|
|
||||||
|
GList *cache_domains;
|
||||||
|
GList *interrupts;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct cache_domain {
|
||||||
|
uint64_t workload;
|
||||||
|
int number;
|
||||||
|
|
||||||
|
int marker;
|
||||||
|
|
||||||
|
cpumask_t mask;
|
||||||
|
|
||||||
|
cpumask_t package_mask;
|
||||||
|
|
||||||
|
int class_count[7];
|
||||||
|
|
||||||
|
GList *cpu_cores;
|
||||||
|
GList *interrupts;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct cpu_core {
|
||||||
|
uint64_t workload;
|
||||||
|
int number;
|
||||||
|
|
||||||
|
int marker;
|
||||||
|
|
||||||
|
int class_count[7];
|
||||||
|
|
||||||
|
cpumask_t package_mask;
|
||||||
|
cpumask_t cache_mask;
|
||||||
|
cpumask_t mask;
|
||||||
|
|
||||||
|
GList *interrupts;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct interrupt {
|
||||||
|
uint64_t workload;
|
||||||
|
|
||||||
|
int balance_level;
|
||||||
|
|
||||||
|
int number;
|
||||||
|
int class;
|
||||||
|
|
||||||
|
uint64_t count;
|
||||||
|
uint64_t old_count;
|
||||||
|
uint64_t extra;
|
||||||
|
|
||||||
|
cpumask_t mask;
|
||||||
|
cpumask_t old_mask;
|
||||||
|
|
||||||
|
|
||||||
|
cpumask_t numa_mask;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
Loading…
Reference in New Issue