/* * 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 "config.h" #include #include #include #include #include #include "irqbalance.h" #define SYSFS_NODE_PATH "/sys/devices/system/node" GList *numa_nodes = NULL; static struct topo_obj unspecified_node = { .load = 0, .number = -1, .obj_type = OBJ_TYPE_NODE, .mask = CPU_MASK_ALL, .interrupts = NULL, .children = NULL, .parent = NULL, .obj_type_list = &numa_nodes, }; static void add_one_node(const char *nodename) { char path[PATH_MAX]; struct topo_obj *new; char *cpustr; FILE *f; int ret; new = calloc(1, sizeof(struct topo_obj)); if (!new) return; sprintf(path, "%s/%s/cpumap", SYSFS_NODE_PATH, nodename); f = fopen(path, "r"); if (ferror(f)) { cpus_clear(new->mask); } else { ret = fscanf(f, "%as", &cpustr); if (!ret || !cpustr) { cpus_clear(new->mask); } else { cpumask_parse_user(cpustr, strlen(cpustr), new->mask); free(cpustr); } } fclose(f); new->obj_type = OBJ_TYPE_NODE; new->number = strtoul(&nodename[4], NULL, 10); new->obj_type_list = &numa_nodes; numa_nodes = g_list_append(numa_nodes, new); } void build_numa_node_list(void) { DIR *dir = opendir(SYSFS_NODE_PATH); struct dirent *entry; /* * Add the unspecified node */ numa_nodes = g_list_append(numa_nodes, &unspecified_node); if (!dir) return; do { entry = readdir(dir); if (!entry) break; if ((entry->d_type == DT_DIR) && (strstr(entry->d_name, "node"))) { add_one_node(entry->d_name); } } while (entry); closedir(dir); } static void free_numa_node(gpointer data) { struct topo_obj *obj = data; g_list_free(obj->children); g_list_free(obj->interrupts); if (data != &unspecified_node) free(data); } void free_numa_node_list(void) { g_list_free_full(numa_nodes, free_numa_node); numa_nodes = NULL; } static gint compare_node(gconstpointer a, gconstpointer b) { const struct topo_obj *ai = a; const struct topo_obj *bi = b; return (ai->number == bi->number) ? 0 : 1; } void add_package_to_node(struct topo_obj *p, int nodeid) { struct topo_obj *node; node = get_numa_node(nodeid); if (!node) { if (debug_mode) printf("Could not find numa node for node id %d\n", nodeid); return; } if (!p->parent) { node->children = g_list_append(node->children, p); p->parent = node; } } void dump_numa_node_info(struct topo_obj *d, void *unused __attribute__((unused))) { char buffer[4096]; printf("NUMA NODE NUMBER: %d\n", d->number); cpumask_scnprintf(buffer, 4096, d->mask); printf("LOCAL CPU MASK: %s\n", buffer); printf("\n"); } struct topo_obj *get_numa_node(int nodeid) { struct topo_obj find; GList *entry; if (!numa_avail) return &unspecified_node; if (nodeid == -1) return &unspecified_node; find.number = nodeid; entry = g_list_find_custom(numa_nodes, &find, compare_node); return entry ? entry->data : NULL; }