327 lines
8.2 KiB
C
327 lines
8.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
|
|
*/
|
|
#include "config.h"
|
|
#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;
|
|
|
|
/* if the package has no cpus in the allowed mask.. just block */
|
|
if (!cpus_intersects(irq->allowed_mask, package->mask))
|
|
bonus += 600000;
|
|
|
|
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];
|
|
|
|
/* if the cache domain has no cpus in the allowed mask.. just block */
|
|
if (!cpus_intersects(irq->allowed_mask, cache_domain->mask))
|
|
bonus += 600000;
|
|
|
|
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];
|
|
|
|
/* if the core has no cpus in the allowed mask.. just block */
|
|
if (!cpus_intersects(irq->allowed_mask, cpu->mask))
|
|
bonus += 600000;
|
|
|
|
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);
|
|
}
|
|
}
|