irqbalance/irqlist.c

281 lines
6.2 KiB
C

/*
* 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 "config.h"
#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 , count = 0;
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) {
char *line = NULL;
size_t size = 0;
FILE *file;
sprintf(buf, "/proc/irq/%i/smp_affinity", number);
file = fopen(buf, "r");
if (!file)
continue;
if (getline(&line, &size, file)==0) {
free(line);
fclose(file);
continue;
}
cpumask_parse_user(line, strlen(line), irq->mask);
fclose(file);
free(line);
} else if (strcmp(entry->d_name,"allowed_affinity")==0) {
char *line = NULL;
size_t size = 0;
FILE *file;
sprintf(buf, "/proc/irq/%i/allowed_affinity", number);
file = fopen(buf, "r");
if (!file)
continue;
if (getline(&line, &size, file)==0) {
free(line);
fclose(file);
continue;
}
cpumask_parse_user(line, strlen(line), irq->allowed_mask);
fclose(file);
free(line);
} else {
irq->class = find_class(irq, entry->d_name);
}
} while (entry);
closedir(dir);
irq->balance_level = map_class_to_level[irq->class];
for (nr = 0; nr < NR_CPUS; nr++)
if (cpu_isset(nr, irq->allowed_mask))
count++;
/* if there is no choice in the allowed mask, don't bother to balance */
if (count<2)
irq->balance_level = BALANCE_NONE;
/* next, check the IRQBALANCE_BANNED_INTERRUPTS env variable for blacklisted irqs */
c = c2 = getenv("IRQBALANCE_BANNED_INTERRUPTS");
if (!c)
return;
do {
c = c2;
nr = strtoul(c, &c2, 10);
if (c!=c2 && nr == number)
irq->balance_level = BALANCE_NONE;
} 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)
{
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;
irq->allowed_mask = CPU_MASK_ALL;
investigate(irq, number);
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);
}