aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/irq/matrix.c
diff options
context:
space:
mode:
authorThomas Gleixner <tglx@linutronix.de>2017-09-13 17:29:14 -0400
committerThomas Gleixner <tglx@linutronix.de>2017-09-25 14:38:26 -0400
commit2f75d9e1c90511bff6d1ce4de94503cc28fec032 (patch)
treeeb17ed8cb0cbd6c10cc3119267e790d8a4a1bb8c /kernel/irq/matrix.c
parent22d0b12f3560d3b3264ee79faa1c05a5060fb916 (diff)
genirq: Implement bitmap matrix allocator
Implement the infrastructure for a simple bitmap based allocator, which will replace the x86 vector allocator. It's in the core code as other architectures might be able to reuse/extend it. For now it only implements allocations for single CPUs, but it's simple to add multi CPU allocation support if required. The concept is rather simple: Global information: system_vector bitmap global accounting PerCPU information: allocation bitmap managed allocation bitmap local accounting The system vector bitmap is used to exclude vectors system wide from the allocation space. The allocation bitmap is used to keep track of per cpu used vectors. The managed allocation bitmap is used to reserve vectors for managed interrupts. When a regular (non managed) interrupt allocation happens then the following rule applies: tmpmap = system_map | alloc_map | managed_map find_zero_bit(tmpmap) Oring the bitmaps together gives the real available space. The same rule applies for reserving a managed interrupt vector. But contrary to the regular interrupts the reservation only marks the bit in the managed map and therefor excludes it from the regular allocations. The managed map is only cleaned out when the a managed interrupt is completely released and it stays alive accross CPU offline/online operations. For managed interrupt allocations the rule is: tmpmap = managed_map & ~alloc_map find_first_bit(tmpmap) This returns the first bit which is in the managed map, but not yet allocated in the allocation map. The allocation marks it in the allocation map and hands it back to the caller for use. The rest of the code are helper functions to handle the various requirements and the accounting which are necessary to replace the x86 vector allocation code. The result is a single patch as the evolution of this infrastructure cannot be represented in bits and pieces. Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Tested-by: Juergen Gross <jgross@suse.com> Tested-by: Yu Chen <yu.c.chen@intel.com> Acked-by: Juergen Gross <jgross@suse.com> Cc: Boris Ostrovsky <boris.ostrovsky@oracle.com> Cc: Tony Luck <tony.luck@intel.com> Cc: Marc Zyngier <marc.zyngier@arm.com> Cc: Alok Kataria <akataria@vmware.com> Cc: Joerg Roedel <joro@8bytes.org> Cc: "Rafael J. Wysocki" <rjw@rjwysocki.net> Cc: Steven Rostedt <rostedt@goodmis.org> Cc: Christoph Hellwig <hch@lst.de> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Borislav Petkov <bp@alien8.de> Cc: Paolo Bonzini <pbonzini@redhat.com> Cc: Rui Zhang <rui.zhang@intel.com> Cc: "K. Y. Srinivasan" <kys@microsoft.com> Cc: Arjan van de Ven <arjan@linux.intel.com> Cc: Dan Williams <dan.j.williams@intel.com> Cc: Chris Metcalf <cmetcalf@mellanox.com> Cc: Len Brown <lenb@kernel.org> Link: https://lkml.kernel.org/r/20170913213153.185437174@linutronix.de
Diffstat (limited to 'kernel/irq/matrix.c')
-rw-r--r--kernel/irq/matrix.c428
1 files changed, 428 insertions, 0 deletions
diff --git a/kernel/irq/matrix.c b/kernel/irq/matrix.c
new file mode 100644
index 000000000000..7b2b4fbde1e2
--- /dev/null
+++ b/kernel/irq/matrix.c
@@ -0,0 +1,428 @@
1/*
2 * Copyright (C) 2017 Thomas Gleixner <tglx@linutronix.de>
3 *
4 * SPDX-License-Identifier: GPL-2.0
5 */
6#include <linux/spinlock.h>
7#include <linux/seq_file.h>
8#include <linux/bitmap.h>
9#include <linux/percpu.h>
10#include <linux/cpu.h>
11#include <linux/irq.h>
12
13#define IRQ_MATRIX_SIZE (BITS_TO_LONGS(IRQ_MATRIX_BITS) * sizeof(unsigned long))
14
15struct cpumap {
16 unsigned int available;
17 unsigned int allocated;
18 unsigned int managed;
19 bool online;
20 unsigned long alloc_map[IRQ_MATRIX_SIZE];
21 unsigned long managed_map[IRQ_MATRIX_SIZE];
22};
23
24struct irq_matrix {
25 unsigned int matrix_bits;
26 unsigned int alloc_start;
27 unsigned int alloc_end;
28 unsigned int alloc_size;
29 unsigned int global_available;
30 unsigned int global_reserved;
31 unsigned int systembits_inalloc;
32 unsigned int total_allocated;
33 unsigned int online_maps;
34 struct cpumap __percpu *maps;
35 unsigned long scratch_map[IRQ_MATRIX_SIZE];
36 unsigned long system_map[IRQ_MATRIX_SIZE];
37};
38
39/**
40 * irq_alloc_matrix - Allocate a irq_matrix structure and initialize it
41 * @matrix_bits: Number of matrix bits must be <= IRQ_MATRIX_BITS
42 * @alloc_start: From which bit the allocation search starts
43 * @alloc_end: At which bit the allocation search ends, i.e first
44 * invalid bit
45 */
46__init struct irq_matrix *irq_alloc_matrix(unsigned int matrix_bits,
47 unsigned int alloc_start,
48 unsigned int alloc_end)
49{
50 struct irq_matrix *m;
51
52 if (matrix_bits > IRQ_MATRIX_BITS)
53 return NULL;
54
55 m = kzalloc(sizeof(*m), GFP_KERNEL);
56 if (!m)
57 return NULL;
58
59 m->matrix_bits = matrix_bits;
60 m->alloc_start = alloc_start;
61 m->alloc_end = alloc_end;
62 m->alloc_size = alloc_end - alloc_start;
63 m->maps = alloc_percpu(*m->maps);
64 if (!m->maps) {
65 kfree(m);
66 return NULL;
67 }
68 return m;
69}
70
71/**
72 * irq_matrix_online - Bring the local CPU matrix online
73 * @m: Matrix pointer
74 */
75void irq_matrix_online(struct irq_matrix *m)
76{
77 struct cpumap *cm = this_cpu_ptr(m->maps);
78
79 BUG_ON(cm->online);
80
81 bitmap_zero(cm->alloc_map, m->matrix_bits);
82 cm->available = m->alloc_size - (cm->managed + m->systembits_inalloc);
83 cm->allocated = 0;
84 m->global_available += cm->available;
85 cm->online = true;
86 m->online_maps++;
87}
88
89/**
90 * irq_matrix_offline - Bring the local CPU matrix offline
91 * @m: Matrix pointer
92 */
93void irq_matrix_offline(struct irq_matrix *m)
94{
95 struct cpumap *cm = this_cpu_ptr(m->maps);
96
97 /* Update the global available size */
98 m->global_available -= cm->available;
99 cm->online = false;
100 m->online_maps--;
101}
102
103static unsigned int matrix_alloc_area(struct irq_matrix *m, struct cpumap *cm,
104 unsigned int num, bool managed)
105{
106 unsigned int area, start = m->alloc_start;
107 unsigned int end = m->alloc_end;
108
109 bitmap_or(m->scratch_map, cm->managed_map, m->system_map, end);
110 bitmap_or(m->scratch_map, m->scratch_map, cm->alloc_map, end);
111 area = bitmap_find_next_zero_area(m->scratch_map, end, start, num, 0);
112 if (area >= end)
113 return area;
114 if (managed)
115 bitmap_set(cm->managed_map, area, num);
116 else
117 bitmap_set(cm->alloc_map, area, num);
118 return area;
119}
120
121/**
122 * irq_matrix_assign_system - Assign system wide entry in the matrix
123 * @m: Matrix pointer
124 * @bit: Which bit to reserve
125 * @replace: Replace an already allocated vector with a system
126 * vector at the same bit position.
127 *
128 * The BUG_ON()s below are on purpose. If this goes wrong in the
129 * early boot process, then the chance to survive is about zero.
130 * If this happens when the system is life, it's not much better.
131 */
132void irq_matrix_assign_system(struct irq_matrix *m, unsigned int bit,
133 bool replace)
134{
135 struct cpumap *cm = this_cpu_ptr(m->maps);
136
137 BUG_ON(bit > m->matrix_bits);
138 BUG_ON(m->online_maps > 1 || (m->online_maps && !replace));
139
140 set_bit(bit, m->system_map);
141 if (replace) {
142 BUG_ON(!test_and_clear_bit(bit, cm->alloc_map));
143 cm->allocated--;
144 m->total_allocated--;
145 }
146 if (bit >= m->alloc_start && bit < m->alloc_end)
147 m->systembits_inalloc++;
148}
149
150/**
151 * irq_matrix_reserve_managed - Reserve a managed interrupt in a CPU map
152 * @m: Matrix pointer
153 * @msk: On which CPUs the bits should be reserved.
154 *
155 * Can be called for offline CPUs. Note, this will only reserve one bit
156 * on all CPUs in @msk, but it's not guaranteed that the bits are at the
157 * same offset on all CPUs
158 */
159int irq_matrix_reserve_managed(struct irq_matrix *m, const struct cpumask *msk)
160{
161 unsigned int cpu, failed_cpu;
162
163 for_each_cpu(cpu, msk) {
164 struct cpumap *cm = per_cpu_ptr(m->maps, cpu);
165 unsigned int bit;
166
167 bit = matrix_alloc_area(m, cm, 1, true);
168 if (bit >= m->alloc_end)
169 goto cleanup;
170 cm->managed++;
171 if (cm->online) {
172 cm->available--;
173 m->global_available--;
174 }
175 }
176 return 0;
177cleanup:
178 failed_cpu = cpu;
179 for_each_cpu(cpu, msk) {
180 if (cpu == failed_cpu)
181 break;
182 irq_matrix_remove_managed(m, cpumask_of(cpu));
183 }
184 return -ENOSPC;
185}
186
187/**
188 * irq_matrix_remove_managed - Remove managed interrupts in a CPU map
189 * @m: Matrix pointer
190 * @msk: On which CPUs the bits should be removed
191 *
192 * Can be called for offline CPUs
193 *
194 * This removes not allocated managed interrupts from the map. It does
195 * not matter which one because the managed interrupts free their
196 * allocation when they shut down. If not, the accounting is screwed,
197 * but all what can be done at this point is warn about it.
198 */
199void irq_matrix_remove_managed(struct irq_matrix *m, const struct cpumask *msk)
200{
201 unsigned int cpu;
202
203 for_each_cpu(cpu, msk) {
204 struct cpumap *cm = per_cpu_ptr(m->maps, cpu);
205 unsigned int bit, end = m->alloc_end;
206
207 if (WARN_ON_ONCE(!cm->managed))
208 continue;
209
210 /* Get managed bit which are not allocated */
211 bitmap_andnot(m->scratch_map, cm->managed_map, cm->alloc_map, end);
212
213 bit = find_first_bit(m->scratch_map, end);
214 if (WARN_ON_ONCE(bit >= end))
215 continue;
216
217 clear_bit(bit, cm->managed_map);
218
219 cm->managed--;
220 if (cm->online) {
221 cm->available++;
222 m->global_available++;
223 }
224 }
225}
226
227/**
228 * irq_matrix_alloc_managed - Allocate a managed interrupt in a CPU map
229 * @m: Matrix pointer
230 * @cpu: On which CPU the interrupt should be allocated
231 */
232int irq_matrix_alloc_managed(struct irq_matrix *m, unsigned int cpu)
233{
234 struct cpumap *cm = per_cpu_ptr(m->maps, cpu);
235 unsigned int bit, end = m->alloc_end;
236
237 /* Get managed bit which are not allocated */
238 bitmap_andnot(m->scratch_map, cm->managed_map, cm->alloc_map, end);
239 bit = find_first_bit(m->scratch_map, end);
240 if (bit >= end)
241 return -ENOSPC;
242 set_bit(bit, cm->alloc_map);
243 cm->allocated++;
244 m->total_allocated++;
245 return bit;
246}
247
248/**
249 * irq_matrix_assign - Assign a preallocated interrupt in the local CPU map
250 * @m: Matrix pointer
251 * @bit: Which bit to mark
252 *
253 * This should only be used to mark preallocated vectors
254 */
255void irq_matrix_assign(struct irq_matrix *m, unsigned int bit)
256{
257 struct cpumap *cm = this_cpu_ptr(m->maps);
258
259 if (WARN_ON_ONCE(bit < m->alloc_start || bit >= m->alloc_end))
260 return;
261 if (WARN_ON_ONCE(test_and_set_bit(bit, cm->alloc_map)))
262 return;
263 cm->allocated++;
264 m->total_allocated++;
265 cm->available--;
266 m->global_available--;
267}
268
269/**
270 * irq_matrix_reserve - Reserve interrupts
271 * @m: Matrix pointer
272 *
273 * This is merily a book keeping call. It increments the number of globally
274 * reserved interrupt bits w/o actually allocating them. This allows to
275 * setup interrupt descriptors w/o assigning low level resources to it.
276 * The actual allocation happens when the interrupt gets activated.
277 */
278void irq_matrix_reserve(struct irq_matrix *m)
279{
280 if (m->global_reserved <= m->global_available &&
281 m->global_reserved + 1 > m->global_available)
282 pr_warn("Interrupt reservation exceeds available resources\n");
283
284 m->global_reserved++;
285}
286
287/**
288 * irq_matrix_remove_reserved - Remove interrupt reservation
289 * @m: Matrix pointer
290 *
291 * This is merily a book keeping call. It decrements the number of globally
292 * reserved interrupt bits. This is used to undo irq_matrix_reserve() when the
293 * interrupt was never in use and a real vector allocated, which undid the
294 * reservation.
295 */
296void irq_matrix_remove_reserved(struct irq_matrix *m)
297{
298 m->global_reserved--;
299}
300
301/**
302 * irq_matrix_alloc - Allocate a regular interrupt in a CPU map
303 * @m: Matrix pointer
304 * @msk: Which CPUs to search in
305 * @reserved: Allocate previously reserved interrupts
306 * @mapped_cpu: Pointer to store the CPU for which the irq was allocated
307 */
308int irq_matrix_alloc(struct irq_matrix *m, const struct cpumask *msk,
309 bool reserved, unsigned int *mapped_cpu)
310{
311 unsigned int cpu;
312
313 for_each_cpu(cpu, msk) {
314 struct cpumap *cm = per_cpu_ptr(m->maps, cpu);
315 unsigned int bit;
316
317 if (!cm->online)
318 continue;
319
320 bit = matrix_alloc_area(m, cm, 1, false);
321 if (bit < m->alloc_end) {
322 cm->allocated++;
323 cm->available--;
324 m->total_allocated++;
325 m->global_available--;
326 if (reserved)
327 m->global_reserved--;
328 *mapped_cpu = cpu;
329 return bit;
330 }
331 }
332 return -ENOSPC;
333}
334
335/**
336 * irq_matrix_free - Free allocated interrupt in the matrix
337 * @m: Matrix pointer
338 * @cpu: Which CPU map needs be updated
339 * @bit: The bit to remove
340 * @managed: If true, the interrupt is managed and not accounted
341 * as available.
342 */
343void irq_matrix_free(struct irq_matrix *m, unsigned int cpu,
344 unsigned int bit, bool managed)
345{
346 struct cpumap *cm = per_cpu_ptr(m->maps, cpu);
347
348 if (WARN_ON_ONCE(bit < m->alloc_start || bit >= m->alloc_end))
349 return;
350
351 if (cm->online) {
352 clear_bit(bit, cm->alloc_map);
353 cm->allocated--;
354 m->total_allocated--;
355 if (!managed) {
356 cm->available++;
357 m->global_available++;
358 }
359 }
360}
361
362/**
363 * irq_matrix_available - Get the number of globally available irqs
364 * @m: Pointer to the matrix to query
365 * @cpudown: If true, the local CPU is about to go down, adjust
366 * the number of available irqs accordingly
367 */
368unsigned int irq_matrix_available(struct irq_matrix *m, bool cpudown)
369{
370 struct cpumap *cm = this_cpu_ptr(m->maps);
371
372 return m->global_available - cpudown ? cm->available : 0;
373}
374
375/**
376 * irq_matrix_reserved - Get the number of globally reserved irqs
377 * @m: Pointer to the matrix to query
378 */
379unsigned int irq_matrix_reserved(struct irq_matrix *m)
380{
381 return m->global_reserved;
382}
383
384/**
385 * irq_matrix_allocated - Get the number of allocated irqs on the local cpu
386 * @m: Pointer to the matrix to search
387 *
388 * This returns number of allocated irqs
389 */
390unsigned int irq_matrix_allocated(struct irq_matrix *m)
391{
392 struct cpumap *cm = this_cpu_ptr(m->maps);
393
394 return cm->allocated;
395}
396
397#ifdef CONFIG_GENERIC_IRQ_DEBUGFS
398/**
399 * irq_matrix_debug_show - Show detailed allocation information
400 * @sf: Pointer to the seq_file to print to
401 * @m: Pointer to the matrix allocator
402 * @ind: Indentation for the print format
403 *
404 * Note, this is a lockless snapshot.
405 */
406void irq_matrix_debug_show(struct seq_file *sf, struct irq_matrix *m, int ind)
407{
408 unsigned int nsys = bitmap_weight(m->system_map, m->matrix_bits);
409 int cpu;
410
411 seq_printf(sf, "Online bitmaps: %6u\n", m->online_maps);
412 seq_printf(sf, "Global available: %6u\n", m->global_available);
413 seq_printf(sf, "Global reserved: %6u\n", m->global_reserved);
414 seq_printf(sf, "Total allocated: %6u\n", m->total_allocated);
415 seq_printf(sf, "System: %u: %*pbl\n", nsys, m->matrix_bits,
416 m->system_map);
417 seq_printf(sf, "%*s| CPU | avl | man | act | vectors\n", ind, " ");
418 cpus_read_lock();
419 for_each_online_cpu(cpu) {
420 struct cpumap *cm = per_cpu_ptr(m->maps, cpu);
421
422 seq_printf(sf, "%*s %4d %4u %4u %4u %*pbl\n", ind, " ",
423 cpu, cm->available, cm->managed, cm->allocated,
424 m->matrix_bits, cm->alloc_map);
425 }
426 cpus_read_unlock();
427}
428#endif