/* * Copyright (C) 2006, Intel Corporation * Copyright (C) 2012, Neil Horman * * 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 #include "irqbalance.h" #define SYSFS_NODE_PATH "/sys/devices/system/node" GList *numa_nodes = NULL; static void add_one_node(int nodeid) { char path[PATH_MAX]; struct topo_obj *new; new = calloc(1, sizeof(struct topo_obj)); if (!new) { need_rebuild = 1; return; } if (nodeid == NUMA_NO_NODE) { cpus_setall(new->mask); } else { cpus_clear(new->mask); sprintf(path, "%s/node%d/cpumap", SYSFS_NODE_PATH, nodeid); process_one_line(path, get_mask_from_bitmap, &new->mask); } new->obj_type = OBJ_TYPE_NODE; new->number = nodeid; new->obj_type_list = &numa_nodes; numa_nodes = g_list_append(numa_nodes, new); } void build_numa_node_list(void) { DIR *dir; struct dirent *entry; /* Add the unspecified node */ add_one_node(NUMA_NO_NODE); if (!numa_avail) return; dir = opendir(SYSFS_NODE_PATH); if (!dir) return; do { entry = readdir(dir); if (!entry) break; if ((entry->d_type == DT_DIR) && (strncmp(entry->d_name, "node", 4) == 0) && isdigit(entry->d_name[4])) { add_one_node(strtoul(&entry->d_name[4], NULL, 10)); } } while (entry); closedir(dir); } void free_numa_node_list(void) { g_list_free_full(numa_nodes, free_cpu_topo); 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 connect_cpu_mem_topo(struct topo_obj *p, void *data __attribute__((unused))) { GList *entry; struct topo_obj *node; int len; len = g_list_length(p->numa_nodes); if (len == 0) { return; } else if (len > 1) { for_each_object(p->children, connect_cpu_mem_topo, NULL); return; } entry = g_list_first(p->numa_nodes); node = entry->data; if (p->obj_type == OBJ_TYPE_PACKAGE && !p->parent) p->parent = node; entry = g_list_find(node->children, p); if (!entry) node->children = g_list_append(node->children, p); } void dump_numa_node_info(struct topo_obj *d, void *unused __attribute__((unused))) { char buffer[4096]; log(TO_CONSOLE, LOG_INFO, "NUMA NODE NUMBER: %d\n", d->number); cpumask_scnprintf(buffer, 4096, d->mask); log(TO_CONSOLE, LOG_INFO, "LOCAL CPU MASK: %s\n", buffer); log(TO_CONSOLE, LOG_INFO, "\n"); } struct topo_obj *get_numa_node(int nodeid) { struct topo_obj find; GList *entry; find.number = numa_avail ? nodeid : NUMA_NO_NODE; entry = g_list_find_custom(numa_nodes, &find, compare_node); return entry ? entry->data : NULL; }