/* * arch/ia64/kernel/domain.c * Architecture specific sched-domains builder. * * Copyright (C) 2004 Jesse Barnes * Copyright (C) 2004 Silicon Graphics, Inc. */ #include #include #include #include #include #include #include #define SD_NODES_PER_DOMAIN 6 #ifdef CONFIG_NUMA /** * find_next_best_node - find the next node to include in a sched_domain * @node: node whose sched_domain we're building * @used_nodes: nodes already in the sched_domain * * Find the next node to include in a given scheduling domain. Simply * finds the closest node not already in the @used_nodes map. * * Should use nodemask_t. */ static int __devinit find_next_best_node(int node, unsigned long *used_nodes) { int i, n, val, min_val, best_node = 0; min_val = INT_MAX; for (i = 0; i < MAX_NUMNODES; i++) { /* Start at @node */ n = (node + i) % MAX_NUMNODES; if (!nr_cpus_node(n)) continue; /* Skip already used nodes */ if (test_bit(n, used_nodes)) continue; /* Simple min distance search */ val = node_distance(node, n); if (val < min_val) { min_val = val; best_node = n; } } set_bit(best_node, used_nodes); return best_node; } /** * sched_domain_node_span - get a cpumask for a node's sched_domain * @node: node whose cpumask we're constructing * @size: number of nodes to include in this span * * Given a node, construct a good cpumask for its sched_domain to span. It * should be one that prevents unnecessary balancing, but also spreads tasks * out optimally. */ static cpumask_t __devinit sched_domain_node_span(int node) { int i; cpumask_t span, nodemask; DECLARE_BITMAP(used_nodes, MAX_NUMNODES); cpus_clear(span); bitmap_zero(used_nodes, MAX_NUMNODES); nodemask = node_to_cpumask(node); cpus_or(span, span, nodemask); set_bit(node, used_nodes); for (i = 1; i < SD_NODES_PER_DOMAIN; i++) { int next_node = find_next_best_node(node, used_nodes); nodemask = node_to_cpumask(next_node); cpus_or(span, span, nodemask); } return span; } #endif /* * At the moment, CONFIG_SCHED_SMT is never defined, but leave it in so we * can switch it on easily if needed. */ #ifdef CONFIG_SCHED_SMT static DEFINE_PER_CPU(struct sched_domain, cpu_domains); static struct sched_group sched_group_cpus[NR_CPUS]; static int __devinit cpu_to_cpu_group(int cpu) { return cpu; } #endif static DEFINE_PER_CPU(struct sched_domain, phys_domains); static struct sched_group sched_group_phys[NR_CPUS]; static int __devinit cpu_to_phys_group(int cpu) { #ifdef CONFIG_SCHED_SMT return first_cpu(cpu_sibling_map[cpu]); #else return cpu; #endif } #ifdef CONFIG_NUMA /* * The init_sched_build_groups can't handle what we want to do with node * groups, so roll our own. Now each node has its own list of groups which * gets dynamically allocated. */ static DEFINE_PER_CPU(struct sched_domain, node_domains); static struct sched_group *sched_group_nodes[MAX_NUMNODES]; static DEFINE_PER_CPU(struct sched_domain, allnodes_domains); static struct sched_group sched_group_allnodes[MAX_NUMNODES]; static int __devinit cpu_to_allnodes_group(int cpu) { return cpu_to_node(cpu); } #endif /* * Set up scheduler domains and groups. Callers must hold the hotplug lock. */ void __devinit arch_init_sched_domains(void) { int i; cpumask_t cpu_default_map; /* * Setup mask for cpus without special case scheduling requirements. * For now this just excludes isolated cpus, but could be used to * exclude other special cases in the future. */ cpus_complement(cpu_default_map, cpu_isolated_map); cpus_and(cpu_default_map, cpu_default_map, cpu_online_map); /* * Set up domains. Isolated domains just stay on the dummy domain. */ for_each_cpu_mask(i, cpu_default_map) { int group; struct sched_domain *sd = NULL, *p; cpumask_t nodemask = node_to_cpumask(cpu_to_node(i)); cpus_and(nodemask, nodemask, cpu_default_map); #ifdef CONFIG_NUMA if (num_online_cpus() > SD_NODES_PER_DOMAIN*cpus_weight(nodemask)) { sd = &per_cpu(allnodes_domains, i); *sd = SD_ALLNODES_INIT; sd->span = cpu_default_map; group = cpu_to_allnodes_group(i); sd->groups = &sched_group_allnodes[group]; p = sd; } else p = NULL; sd = &per_cpu(node_domains, i); *sd = SD_NODE_INIT; sd->span = sched_domain_node_span(cpu_to_node(i)); sd->parent = p; cpus_and(sd->span, sd->span, cpu_default_map); #endif p = sd; sd = &per_cpu(phys_domains, i); group = cpu_to_phys_group(i); *sd = SD_CPU_INIT; sd->span = nodemask; sd->parent = p; sd->groups = &sched_group_phys[group]; #ifdef CONFIG_SCHED_SMT p = sd; sd = &per_cpu(cpu_domains, i); group = cpu_to_cpu_group(i); *sd = SD_SIBLING_INIT; sd->span = cpu_sibling_map[i]; cpus_and(sd->span, sd->span, cpu_default_map); sd->parent = p; sd->groups = &sched_group_cpus[group]; #endif } #ifdef CONFIG_SCHED_SMT /* Set up CPU (sibling) groups */ for_each_cpu_mask(i, cpu_default_map) { cpumask_t this_sibling_map = cpu_sibling_map[i]; cpus_and(this_sibling_map, this_sibling_map, cpu_default_map); if (i != first_cpu(this_sibling_map)) continue; init_sched_build_groups(sched_group_cpus, this_sibling_map, &cpu_to_cpu_group); } #endif /* Set up physical groups */ for (i = 0; i < MAX_NUMNODES; i++) { cpumask_t nodemask = node_to_cpumask(i); cpus_and(nodemask, nodemask, cpu_default_map); if (cpus_empty(nodemask)) continue; init_sched_build_groups(sched_group_phys, nodemask, &cpu_to_phys_group); } #ifdef CONFIG_NUMA init_sched_build_groups(sched_group_allnodes, cpu_default_map, &cpu_to_allnodes_group); for (i = 0; i < MAX_NUMNODES; i++) { /* Set up node groups */ struct sched_group *sg, *prev; cpumask_t nodemask = node_to_cpumask(i); cpumask_t domainspan; cpumask_t covered = CPU_MASK_NONE; int j; cpus_and(nodemask, nodemask, cpu_default_map); if (cpus_empty(nodemask)) continue; domainspan = sched_domain_node_span(i); cpus_and(domainspan, domainspan, cpu_default_map); sg = kmalloc(sizeof(struct sched_group), GFP_KERNEL); sched_group_nodes[i] = sg; for_each_cpu_mask(j, nodemask) { struct sched_domain *sd; sd = &per_cpu(node_domains, j); sd->groups = sg; if (sd->groups == NULL) { /* Turn off balancing if we have no groups */ sd->flags = 0; } } if (!sg) { printk(KERN_WARNING "Can not alloc domain group for node %d\n", i); continue; } sg->cpu_power = 0; sg->cpumask = nodemask; cpus_or(covered, covered, nodemask); prev = sg; for (j = 0; j < MAX_NUMNODES; j++) { cpumask_t tmp, notcovered; int n = (i + j) % MAX_NUMNODES; cpus_complement(notcovered, covered); cpus_and(tmp, notcovered, cpu_default_map); cpus_and(tmp, tmp, domainspan); if (cpus_empty(tmp)) break; nodemask = node_to_cpumask(n); cpus_and(tmp, tmp, nodemask); if (cpus_empty(tmp)) continue; sg = kmalloc(sizeof(struct sched_group), GFP_KERNEL); if (!sg) { printk(KERN_WARNING "Can not alloc domain group for node %d\n", j); break; } sg->cpu_power = 0; sg->cpumask = tmp; cpus_or(covered, covered, tmp); prev->next = sg; prev = sg; } prev->next = sched_group_nodes[i]; } #endif /* Calculate CPU power for physical packages and nodes */ for_each_cpu_mask(i, cpu_default_map) { int power; struct sched_domain *sd; #ifdef CONFIG_SCHED_SMT sd = &per_cpu(cpu_domains, i); power = SCHED_LOAD_SCALE; sd->groups->cpu_power = power; #endif sd = &per_cpu(phys_domains, i); power = SCHED_LOAD_SCALE + SCHED_LOAD_SCALE * (cpus_weight(sd->groups->cpumask)-1) / 10; sd->groups->cpu_power = power; #ifdef CONFIG_NUMA sd = &per_cpu(allnodes_domains, i); if (sd->groups) { power = SCHED_LOAD_SCALE + SCHED_LOAD_SCALE * (cpus_weight(sd->groups->cpumask)-1) / 10; sd->groups->cpu_power = power; } #endif } #ifdef CONFIG_NUMA for (i = 0; i < MAX_NUMNODES; i++) { struct sched_group *sg = sched_group_nodes[i]; int j; if (sg == NULL) continue; next_sg: for_each_cpu_mask(j, sg->cpumask) { struct sched_domain *sd; int power; sd = &per_cpu(phys_domains, j); if (j != first_cpu(sd->groups->cpumask)) { /* * Only add "power" once for each * physical package. */ continue; } power = SCHED_LOAD_SCALE + SCHED_LOAD_SCALE * (cpus_weight(sd->groups->cpumask)-1) / 10; sg->cpu_power += power; } sg = sg->next; if (sg != sched_group_nodes[i]) goto next_sg; } #endif /* Attach the domains */ for_each_online_cpu(i) { struct sched_domain *sd; #ifdef CONFIG_SCHED_SMT sd = &per_cpu(cpu_domains, i); #else sd = &per_cpu(phys_domains, i); #endif cpu_attach_domain(sd, i); } } void __devinit arch_destroy_sched_domains(void) { #ifdef CONFIG_NUMA int i; for (i = 0; i < MAX_NUMNODES; i++) { struct sched_group *oldsg, *sg = sched_group_nodes[i]; if (sg == NULL) continue; sg = sg->next; next_sg: oldsg = sg; sg = sg->next; kfree(oldsg); if (oldsg != sched_group_nodes[i]) goto next_sg; sched_group_nodes[i] = NULL; } #endif }