Skip to content

Commit 2db1f95

Browse files
committed
x86/vector: Handle managed interrupts proper
Managed interrupts need to reserve interrupt vectors permanently, but as long as the interrupt is deactivated, the vector should not be active. Reserve a new system vector, which can be used to initially initialize MSI/DMAR/IOAPIC entries. In that situation the interrupts are disabled in the corresponding MSI/DMAR/IOAPIC devices. So the vector should never be sent to any CPU. When the managed interrupt is started up, a real vector is assigned from the managed vector space and configured in MSI/DMAR/IOAPIC. This allows a clear separation of inactive and active modes and simplifies the final decisions whether the global vector space is sufficient for CPU offline operations. The vector space can be reserved even on offline CPUs and will survive CPU offline/online operations. 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: Len Brown <lenb@kernel.org> Link: https://lkml.kernel.org/r/20170913213156.104616625@linutronix.de
1 parent 90ad9e2 commit 2db1f95

File tree

2 files changed

+174
-24
lines changed

2 files changed

+174
-24
lines changed

arch/x86/include/asm/irq_vectors.h

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -101,12 +101,8 @@
101101
#define POSTED_INTR_NESTED_VECTOR 0xf0
102102
#endif
103103

104-
/*
105-
* Local APIC timer IRQ vector is on a different priority level,
106-
* to work around the 'lost local interrupt if more than 2 IRQ
107-
* sources per level' errata.
108-
*/
109-
#define LOCAL_TIMER_VECTOR 0xef
104+
#define MANAGED_IRQ_SHUTDOWN_VECTOR 0xef
105+
#define LOCAL_TIMER_VECTOR 0xee
110106

111107
#define NR_VECTORS 256
112108

arch/x86/kernel/apic/vector.c

Lines changed: 172 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,8 @@ struct apic_chip_data {
3232
unsigned int prev_cpu;
3333
unsigned int irq;
3434
struct hlist_node clist;
35-
u8 move_in_progress : 1;
35+
unsigned int move_in_progress : 1,
36+
is_managed : 1;
3637
};
3738

3839
struct irq_domain *x86_vector_domain;
@@ -152,6 +153,28 @@ static void apic_update_vector(struct irq_data *irqd, unsigned int newvec,
152153
per_cpu(vector_irq, newcpu)[newvec] = desc;
153154
}
154155

156+
static void vector_assign_managed_shutdown(struct irq_data *irqd)
157+
{
158+
unsigned int cpu = cpumask_first(cpu_online_mask);
159+
160+
apic_update_irq_cfg(irqd, MANAGED_IRQ_SHUTDOWN_VECTOR, cpu);
161+
}
162+
163+
static int reserve_managed_vector(struct irq_data *irqd)
164+
{
165+
const struct cpumask *affmsk = irq_data_get_affinity_mask(irqd);
166+
struct apic_chip_data *apicd = apic_chip_data(irqd);
167+
unsigned long flags;
168+
int ret;
169+
170+
raw_spin_lock_irqsave(&vector_lock, flags);
171+
apicd->is_managed = true;
172+
ret = irq_matrix_reserve_managed(vector_matrix, affmsk);
173+
raw_spin_unlock_irqrestore(&vector_lock, flags);
174+
trace_vector_reserve_managed(irqd->irq, ret);
175+
return ret;
176+
}
177+
155178
static int allocate_vector(struct irq_data *irqd, const struct cpumask *dest)
156179
{
157180
struct apic_chip_data *apicd = apic_chip_data(irqd);
@@ -200,20 +223,65 @@ static int assign_irq_vector(struct irq_data *irqd, const struct cpumask *dest)
200223
return ret;
201224
}
202225

203-
static int assign_irq_vector_policy(struct irq_data *irqd,
204-
struct irq_alloc_info *info, int node)
226+
static int assign_irq_vector_any_locked(struct irq_data *irqd)
227+
{
228+
int node = irq_data_get_node(irqd);
229+
230+
if (node != NUMA_NO_NODE) {
231+
if (!assign_vector_locked(irqd, cpumask_of_node(node)))
232+
return 0;
233+
}
234+
return assign_vector_locked(irqd, cpu_online_mask);
235+
}
236+
237+
static int assign_irq_vector_any(struct irq_data *irqd)
238+
{
239+
unsigned long flags;
240+
int ret;
241+
242+
raw_spin_lock_irqsave(&vector_lock, flags);
243+
ret = assign_irq_vector_any_locked(irqd);
244+
raw_spin_unlock_irqrestore(&vector_lock, flags);
245+
return ret;
246+
}
247+
248+
static int
249+
assign_irq_vector_policy(struct irq_data *irqd, struct irq_alloc_info *info)
205250
{
251+
if (irqd_affinity_is_managed(irqd))
252+
return reserve_managed_vector(irqd);
206253
if (info->mask)
207254
return assign_irq_vector(irqd, info->mask);
208-
if (node != NUMA_NO_NODE &&
209-
!assign_irq_vector(irqd, cpumask_of_node(node)))
255+
return assign_irq_vector_any(irqd);
256+
}
257+
258+
static int
259+
assign_managed_vector(struct irq_data *irqd, const struct cpumask *dest)
260+
{
261+
const struct cpumask *affmsk = irq_data_get_affinity_mask(irqd);
262+
struct apic_chip_data *apicd = apic_chip_data(irqd);
263+
int vector, cpu;
264+
265+
cpumask_and(vector_searchmask, vector_searchmask, affmsk);
266+
cpu = cpumask_first(vector_searchmask);
267+
if (cpu >= nr_cpu_ids)
268+
return -EINVAL;
269+
/* set_affinity might call here for nothing */
270+
if (apicd->vector && cpumask_test_cpu(apicd->cpu, vector_searchmask))
210271
return 0;
211-
return assign_irq_vector(irqd, cpu_online_mask);
272+
vector = irq_matrix_alloc_managed(vector_matrix, cpu);
273+
trace_vector_alloc_managed(irqd->irq, vector, vector);
274+
if (vector < 0)
275+
return vector;
276+
apic_update_vector(irqd, vector, cpu);
277+
apic_update_irq_cfg(irqd, vector, cpu);
278+
return 0;
212279
}
213280

214281
static void clear_irq_vector(struct irq_data *irqd)
215282
{
216283
struct apic_chip_data *apicd = apic_chip_data(irqd);
284+
bool managed = irqd_affinity_is_managed(irqd);
217285
unsigned int vector = apicd->vector;
218286

219287
lockdep_assert_held(&vector_lock);
@@ -225,7 +293,7 @@ static void clear_irq_vector(struct irq_data *irqd)
225293
apicd->prev_cpu);
226294

227295
per_cpu(vector_irq, apicd->cpu)[vector] = VECTOR_UNUSED;
228-
irq_matrix_free(vector_matrix, apicd->cpu, vector, false);
296+
irq_matrix_free(vector_matrix, apicd->cpu, vector, managed);
229297
apicd->vector = 0;
230298

231299
/* Clean up move in progress */
@@ -234,12 +302,86 @@ static void clear_irq_vector(struct irq_data *irqd)
234302
return;
235303

236304
per_cpu(vector_irq, apicd->prev_cpu)[vector] = VECTOR_UNUSED;
237-
irq_matrix_free(vector_matrix, apicd->prev_cpu, vector, false);
305+
irq_matrix_free(vector_matrix, apicd->prev_cpu, vector, managed);
238306
apicd->prev_vector = 0;
239307
apicd->move_in_progress = 0;
240308
hlist_del_init(&apicd->clist);
241309
}
242310

311+
static void x86_vector_deactivate(struct irq_domain *dom, struct irq_data *irqd)
312+
{
313+
struct apic_chip_data *apicd = apic_chip_data(irqd);
314+
unsigned long flags;
315+
316+
trace_vector_deactivate(irqd->irq, apicd->is_managed,
317+
false, false);
318+
319+
if (apicd->is_managed)
320+
return;
321+
322+
raw_spin_lock_irqsave(&vector_lock, flags);
323+
clear_irq_vector(irqd);
324+
vector_assign_managed_shutdown(irqd);
325+
raw_spin_unlock_irqrestore(&vector_lock, flags);
326+
}
327+
328+
static int activate_managed(struct irq_data *irqd)
329+
{
330+
const struct cpumask *dest = irq_data_get_affinity_mask(irqd);
331+
int ret;
332+
333+
cpumask_and(vector_searchmask, dest, cpu_online_mask);
334+
if (WARN_ON_ONCE(cpumask_empty(vector_searchmask))) {
335+
/* Something in the core code broke! Survive gracefully */
336+
pr_err("Managed startup for irq %u, but no CPU\n", irqd->irq);
337+
return EINVAL;
338+
}
339+
340+
ret = assign_managed_vector(irqd, vector_searchmask);
341+
/*
342+
* This should not happen. The vector reservation got buggered. Handle
343+
* it gracefully.
344+
*/
345+
if (WARN_ON_ONCE(ret < 0)) {
346+
pr_err("Managed startup irq %u, no vector available\n",
347+
irqd->irq);
348+
}
349+
return ret;
350+
}
351+
352+
static int x86_vector_activate(struct irq_domain *dom, struct irq_data *irqd,
353+
bool early)
354+
{
355+
struct apic_chip_data *apicd = apic_chip_data(irqd);
356+
unsigned long flags;
357+
int ret = 0;
358+
359+
trace_vector_activate(irqd->irq, apicd->is_managed,
360+
false, early);
361+
362+
if (!apicd->is_managed)
363+
return 0;
364+
365+
raw_spin_lock_irqsave(&vector_lock, flags);
366+
if (early || irqd_is_managed_and_shutdown(irqd))
367+
vector_assign_managed_shutdown(irqd);
368+
else
369+
ret = activate_managed(irqd);
370+
raw_spin_unlock_irqrestore(&vector_lock, flags);
371+
return ret;
372+
}
373+
374+
static void vector_free_reserved_and_managed(struct irq_data *irqd)
375+
{
376+
const struct cpumask *dest = irq_data_get_affinity_mask(irqd);
377+
struct apic_chip_data *apicd = apic_chip_data(irqd);
378+
379+
trace_vector_teardown(irqd->irq, apicd->is_managed, false);
380+
381+
if (apicd->is_managed)
382+
irq_matrix_remove_managed(vector_matrix, dest);
383+
}
384+
243385
static void x86_vector_free_irqs(struct irq_domain *domain,
244386
unsigned int virq, unsigned int nr_irqs)
245387
{
@@ -253,6 +395,7 @@ static void x86_vector_free_irqs(struct irq_domain *domain,
253395
if (irqd && irqd->chip_data) {
254396
raw_spin_lock_irqsave(&vector_lock, flags);
255397
clear_irq_vector(irqd);
398+
vector_free_reserved_and_managed(irqd);
256399
apicd = irqd->chip_data;
257400
irq_domain_reset_irq_data(irqd);
258401
raw_spin_unlock_irqrestore(&vector_lock, flags);
@@ -310,7 +453,7 @@ static int x86_vector_alloc_irqs(struct irq_domain *domain, unsigned int virq,
310453
continue;
311454
}
312455

313-
err = assign_irq_vector_policy(irqd, info, node);
456+
err = assign_irq_vector_policy(irqd, info);
314457
trace_vector_setup(virq + i, false, err);
315458
if (err)
316459
goto error;
@@ -368,6 +511,8 @@ void x86_vector_debug_show(struct seq_file *m, struct irq_domain *d,
368511
static const struct irq_domain_ops x86_vector_domain_ops = {
369512
.alloc = x86_vector_alloc_irqs,
370513
.free = x86_vector_free_irqs,
514+
.activate = x86_vector_activate,
515+
.deactivate = x86_vector_deactivate,
371516
#ifdef CONFIG_GENERIC_IRQ_DEBUGFS
372517
.debug_show = x86_vector_debug_show,
373518
#endif
@@ -531,13 +676,13 @@ static int apic_set_affinity(struct irq_data *irqd,
531676
{
532677
int err;
533678

534-
if (!IS_ENABLED(CONFIG_SMP))
535-
return -EPERM;
536-
537-
if (!cpumask_intersects(dest, cpu_online_mask))
538-
return -EINVAL;
539-
540-
err = assign_irq_vector(irqd, dest);
679+
raw_spin_lock(&vector_lock);
680+
cpumask_and(vector_searchmask, dest, cpu_online_mask);
681+
if (irqd_affinity_is_managed(irqd))
682+
err = assign_managed_vector(irqd, vector_searchmask);
683+
else
684+
err = assign_vector_locked(irqd, vector_searchmask);
685+
raw_spin_unlock(&vector_lock);
541686
return err ? err : IRQ_SET_MASK_OK;
542687
}
543688

@@ -577,9 +722,18 @@ static void free_moved_vector(struct apic_chip_data *apicd)
577722
{
578723
unsigned int vector = apicd->prev_vector;
579724
unsigned int cpu = apicd->prev_cpu;
725+
bool managed = apicd->is_managed;
726+
727+
/*
728+
* This should never happen. Managed interrupts are not
729+
* migrated except on CPU down, which does not involve the
730+
* cleanup vector. But try to keep the accounting correct
731+
* nevertheless.
732+
*/
733+
WARN_ON_ONCE(managed);
580734

581-
trace_vector_free_moved(apicd->irq, vector, false);
582-
irq_matrix_free(vector_matrix, cpu, vector, false);
735+
trace_vector_free_moved(apicd->irq, vector, managed);
736+
irq_matrix_free(vector_matrix, cpu, vector, managed);
583737
__this_cpu_write(vector_irq[vector], VECTOR_UNUSED);
584738
hlist_del_init(&apicd->clist);
585739
apicd->prev_vector = 0;

0 commit comments

Comments
 (0)