genirq/affinity: Move group_cpus_evenly() into lib/
[platform/kernel/linux-starfive.git] / kernel / irq / affinity.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (C) 2016 Thomas Gleixner.
4  * Copyright (C) 2016-2017 Christoph Hellwig.
5  */
6 #include <linux/interrupt.h>
7 #include <linux/kernel.h>
8 #include <linux/slab.h>
9 #include <linux/cpu.h>
10 #include <linux/group_cpus.h>
11
12 static void default_calc_sets(struct irq_affinity *affd, unsigned int affvecs)
13 {
14         affd->nr_sets = 1;
15         affd->set_size[0] = affvecs;
16 }
17
18 /**
19  * irq_create_affinity_masks - Create affinity masks for multiqueue spreading
20  * @nvecs:      The total number of vectors
21  * @affd:       Description of the affinity requirements
22  *
23  * Returns the irq_affinity_desc pointer or NULL if allocation failed.
24  */
25 struct irq_affinity_desc *
26 irq_create_affinity_masks(unsigned int nvecs, struct irq_affinity *affd)
27 {
28         unsigned int affvecs, curvec, usedvecs, i;
29         struct irq_affinity_desc *masks = NULL;
30
31         /*
32          * Determine the number of vectors which need interrupt affinities
33          * assigned. If the pre/post request exhausts the available vectors
34          * then nothing to do here except for invoking the calc_sets()
35          * callback so the device driver can adjust to the situation.
36          */
37         if (nvecs > affd->pre_vectors + affd->post_vectors)
38                 affvecs = nvecs - affd->pre_vectors - affd->post_vectors;
39         else
40                 affvecs = 0;
41
42         /*
43          * Simple invocations do not provide a calc_sets() callback. Install
44          * the generic one.
45          */
46         if (!affd->calc_sets)
47                 affd->calc_sets = default_calc_sets;
48
49         /* Recalculate the sets */
50         affd->calc_sets(affd, affvecs);
51
52         if (WARN_ON_ONCE(affd->nr_sets > IRQ_AFFINITY_MAX_SETS))
53                 return NULL;
54
55         /* Nothing to assign? */
56         if (!affvecs)
57                 return NULL;
58
59         masks = kcalloc(nvecs, sizeof(*masks), GFP_KERNEL);
60         if (!masks)
61                 return NULL;
62
63         /* Fill out vectors at the beginning that don't need affinity */
64         for (curvec = 0; curvec < affd->pre_vectors; curvec++)
65                 cpumask_copy(&masks[curvec].mask, irq_default_affinity);
66
67         /*
68          * Spread on present CPUs starting from affd->pre_vectors. If we
69          * have multiple sets, build each sets affinity mask separately.
70          */
71         for (i = 0, usedvecs = 0; i < affd->nr_sets; i++) {
72                 unsigned int this_vecs = affd->set_size[i];
73                 int j;
74                 struct cpumask *result = group_cpus_evenly(this_vecs);
75
76                 if (!result) {
77                         kfree(masks);
78                         return NULL;
79                 }
80
81                 for (j = 0; j < this_vecs; j++)
82                         cpumask_copy(&masks[curvec + j].mask, &result[j]);
83                 kfree(result);
84
85                 curvec += this_vecs;
86                 usedvecs += this_vecs;
87         }
88
89         /* Fill out vectors at the end that don't need affinity */
90         if (usedvecs >= affvecs)
91                 curvec = affd->pre_vectors + affvecs;
92         else
93                 curvec = affd->pre_vectors + usedvecs;
94         for (; curvec < nvecs; curvec++)
95                 cpumask_copy(&masks[curvec].mask, irq_default_affinity);
96
97         /* Mark the managed interrupts */
98         for (i = affd->pre_vectors; i < nvecs - affd->post_vectors; i++)
99                 masks[i].is_managed = 1;
100
101         return masks;
102 }
103
104 /**
105  * irq_calc_affinity_vectors - Calculate the optimal number of vectors
106  * @minvec:     The minimum number of vectors available
107  * @maxvec:     The maximum number of vectors available
108  * @affd:       Description of the affinity requirements
109  */
110 unsigned int irq_calc_affinity_vectors(unsigned int minvec, unsigned int maxvec,
111                                        const struct irq_affinity *affd)
112 {
113         unsigned int resv = affd->pre_vectors + affd->post_vectors;
114         unsigned int set_vecs;
115
116         if (resv > minvec)
117                 return 0;
118
119         if (affd->calc_sets) {
120                 set_vecs = maxvec - resv;
121         } else {
122                 cpus_read_lock();
123                 set_vecs = cpumask_weight(cpu_possible_mask);
124                 cpus_read_unlock();
125         }
126
127         return resv + min(set_vecs, maxvec - resv);
128 }