diff options
Diffstat (limited to 'kernel/irq/affinity.c')
| -rw-r--r-- | kernel/irq/affinity.c | 234 |
1 files changed, 71 insertions, 163 deletions
diff --git a/kernel/irq/affinity.c b/kernel/irq/affinity.c index d69bd77252a7..4013e6ad2b2f 100644 --- a/kernel/irq/affinity.c +++ b/kernel/irq/affinity.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2016 Thomas Gleixner. * Copyright (C) 2016-2017 Christoph Hellwig. @@ -6,91 +7,12 @@ #include <linux/kernel.h> #include <linux/slab.h> #include <linux/cpu.h> +#include <linux/group_cpus.h> -static void irq_spread_init_one(struct cpumask *irqmsk, struct cpumask *nmsk, - int cpus_per_vec) +static void default_calc_sets(struct irq_affinity *affd, unsigned int affvecs) { - const struct cpumask *siblmsk; - int cpu, sibl; - - for ( ; cpus_per_vec > 0; ) { - cpu = cpumask_first(nmsk); - - /* Should not happen, but I'm too lazy to think about it */ - if (cpu >= nr_cpu_ids) - return; - - cpumask_clear_cpu(cpu, nmsk); - cpumask_set_cpu(cpu, irqmsk); - cpus_per_vec--; - - /* If the cpu has siblings, use them first */ - siblmsk = topology_sibling_cpumask(cpu); - for (sibl = -1; cpus_per_vec > 0; ) { - sibl = cpumask_next(sibl, siblmsk); - if (sibl >= nr_cpu_ids) - break; - if (!cpumask_test_and_clear_cpu(sibl, nmsk)) - continue; - cpumask_set_cpu(sibl, irqmsk); - cpus_per_vec--; - } - } -} - -static cpumask_var_t *alloc_node_to_present_cpumask(void) -{ - cpumask_var_t *masks; - int node; - - masks = kcalloc(nr_node_ids, sizeof(cpumask_var_t), GFP_KERNEL); - if (!masks) - return NULL; - - for (node = 0; node < nr_node_ids; node++) { - if (!zalloc_cpumask_var(&masks[node], GFP_KERNEL)) - goto out_unwind; - } - - return masks; - -out_unwind: - while (--node >= 0) - free_cpumask_var(masks[node]); - kfree(masks); - return NULL; -} - -static void free_node_to_present_cpumask(cpumask_var_t *masks) -{ - int node; - - for (node = 0; node < nr_node_ids; node++) - free_cpumask_var(masks[node]); - kfree(masks); -} - -static void build_node_to_present_cpumask(cpumask_var_t *masks) -{ - int cpu; - - for_each_present_cpu(cpu) - cpumask_set_cpu(cpu, masks[cpu_to_node(cpu)]); -} - -static int get_nodes_in_cpumask(cpumask_var_t *node_to_present_cpumask, - const struct cpumask *mask, nodemask_t *nodemsk) -{ - int n, nodes = 0; - - /* Calculate the number of nodes in the supplied affinity mask */ - for_each_node(n) { - if (cpumask_intersects(mask, node_to_present_cpumask[n])) { - node_set(n, *nodemsk); - nodes++; - } - } - return nodes; + affd->nr_sets = 1; + affd->set_size[0] = affvecs; } /** @@ -98,102 +20,83 @@ static int get_nodes_in_cpumask(cpumask_var_t *node_to_present_cpumask, * @nvecs: The total number of vectors * @affd: Description of the affinity requirements * - * Returns the masks pointer or NULL if allocation failed. + * Returns the irq_affinity_desc pointer or NULL if allocation failed. */ -struct cpumask * -irq_create_affinity_masks(int nvecs, const struct irq_affinity *affd) +struct irq_affinity_desc * +irq_create_affinity_masks(unsigned int nvecs, struct irq_affinity *affd) { - int n, nodes, cpus_per_vec, extra_vecs, curvec; - int affv = nvecs - affd->pre_vectors - affd->post_vectors; - int last_affv = affv + affd->pre_vectors; - nodemask_t nodemsk = NODE_MASK_NONE; - struct cpumask *masks; - cpumask_var_t nmsk, *node_to_present_cpumask; + unsigned int affvecs, curvec, usedvecs, i; + struct irq_affinity_desc *masks = NULL; + + /* + * Determine the number of vectors which need interrupt affinities + * assigned. If the pre/post request exhausts the available vectors + * then nothing to do here except for invoking the calc_sets() + * callback so the device driver can adjust to the situation. + */ + if (nvecs > affd->pre_vectors + affd->post_vectors) + affvecs = nvecs - affd->pre_vectors - affd->post_vectors; + else + affvecs = 0; /* - * If there aren't any vectors left after applying the pre/post - * vectors don't bother with assigning affinity. + * Simple invocations do not provide a calc_sets() callback. Install + * the generic one. */ - if (!affv) + if (!affd->calc_sets) + affd->calc_sets = default_calc_sets; + + /* Recalculate the sets */ + affd->calc_sets(affd, affvecs); + + if (WARN_ON_ONCE(affd->nr_sets > IRQ_AFFINITY_MAX_SETS)) return NULL; - if (!zalloc_cpumask_var(&nmsk, GFP_KERNEL)) + /* Nothing to assign? */ + if (!affvecs) return NULL; masks = kcalloc(nvecs, sizeof(*masks), GFP_KERNEL); if (!masks) - goto out; - - node_to_present_cpumask = alloc_node_to_present_cpumask(); - if (!node_to_present_cpumask) - goto out; + return NULL; /* Fill out vectors at the beginning that don't need affinity */ for (curvec = 0; curvec < affd->pre_vectors; curvec++) - cpumask_copy(masks + curvec, irq_default_affinity); - - /* Stabilize the cpumasks */ - get_online_cpus(); - build_node_to_present_cpumask(node_to_present_cpumask); - nodes = get_nodes_in_cpumask(node_to_present_cpumask, cpu_present_mask, - &nodemsk); + cpumask_copy(&masks[curvec].mask, irq_default_affinity); /* - * If the number of nodes in the mask is greater than or equal the - * number of vectors we just spread the vectors across the nodes. + * Spread on present CPUs starting from affd->pre_vectors. If we + * have multiple sets, build each sets affinity mask separately. */ - if (affv <= nodes) { - for_each_node_mask(n, nodemsk) { - cpumask_copy(masks + curvec, - node_to_present_cpumask[n]); - if (++curvec == last_affv) - break; - } - goto done; - } + for (i = 0, usedvecs = 0; i < affd->nr_sets; i++) { + unsigned int nr_masks, this_vecs = affd->set_size[i]; + struct cpumask *result = group_cpus_evenly(this_vecs, &nr_masks); - for_each_node_mask(n, nodemsk) { - int ncpus, v, vecs_to_assign, vecs_per_node; - - /* Spread the vectors per node */ - vecs_per_node = (affv - (curvec - affd->pre_vectors)) / nodes; - - /* Get the cpus on this node which are in the mask */ - cpumask_and(nmsk, cpu_present_mask, node_to_present_cpumask[n]); - - /* Calculate the number of cpus per vector */ - ncpus = cpumask_weight(nmsk); - vecs_to_assign = min(vecs_per_node, ncpus); - - /* Account for rounding errors */ - extra_vecs = ncpus - vecs_to_assign * (ncpus / vecs_to_assign); - - for (v = 0; curvec < last_affv && v < vecs_to_assign; - curvec++, v++) { - cpus_per_vec = ncpus / vecs_to_assign; - - /* Account for extra vectors to compensate rounding errors */ - if (extra_vecs) { - cpus_per_vec++; - --extra_vecs; - } - irq_spread_init_one(masks + curvec, nmsk, cpus_per_vec); + if (!result) { + kfree(masks); + return NULL; } - if (curvec >= last_affv) - break; - --nodes; - } + for (int j = 0; j < nr_masks; j++) + cpumask_copy(&masks[curvec + j].mask, &result[j]); + kfree(result); -done: - put_online_cpus(); + curvec += nr_masks; + usedvecs += nr_masks; + } /* Fill out vectors at the end that don't need affinity */ + if (usedvecs >= affvecs) + curvec = affd->pre_vectors + affvecs; + else + curvec = affd->pre_vectors + usedvecs; for (; curvec < nvecs; curvec++) - cpumask_copy(masks + curvec, irq_default_affinity); - free_node_to_present_cpumask(node_to_present_cpumask); -out: - free_cpumask_var(nmsk); + cpumask_copy(&masks[curvec].mask, irq_default_affinity); + + /* Mark the managed interrupts */ + for (i = affd->pre_vectors; i < nvecs - affd->post_vectors; i++) + masks[i].is_managed = 1; + return masks; } @@ -203,17 +106,22 @@ out: * @maxvec: The maximum number of vectors available * @affd: Description of the affinity requirements */ -int irq_calc_affinity_vectors(int minvec, int maxvec, const struct irq_affinity *affd) +unsigned int irq_calc_affinity_vectors(unsigned int minvec, unsigned int maxvec, + const struct irq_affinity *affd) { - int resv = affd->pre_vectors + affd->post_vectors; - int vecs = maxvec - resv; - int ret; + unsigned int resv = affd->pre_vectors + affd->post_vectors; + unsigned int set_vecs; if (resv > minvec) return 0; - get_online_cpus(); - ret = min_t(int, cpumask_weight(cpu_present_mask), vecs) + resv; - put_online_cpus(); - return ret; + if (affd->calc_sets) { + set_vecs = maxvec - resv; + } else { + cpus_read_lock(); + set_vecs = cpumask_weight(cpu_possible_mask); + cpus_read_unlock(); + } + + return resv + min(set_vecs, maxvec - resv); } |
