Interrupts which cannot be migrated in process context, need to be masked before the affinity is changed forcefully. Add support for that. Will be compiled out for architectures which do not have this x86 specific issue. Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Cc: Jens Axboe <axboe@kernel.dk> Cc: Marc Zyngier <marc.zyngier@arm.com> Cc: Michael Ellerman <mpe@ellerman.id.au> Cc: Keith Busch <keith.busch@intel.com> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Christoph Hellwig <hch@lst.de> Link: http://lkml.kernel.org/r/20170619235445.604565591@linutronix.de
124 lines
3.4 KiB
C
124 lines
3.4 KiB
C
/*
|
|
* Generic cpu hotunplug interrupt migration code copied from the
|
|
* arch/arm implementation
|
|
*
|
|
* Copyright (C) Russell King
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
* published by the Free Software Foundation.
|
|
*/
|
|
#include <linux/interrupt.h>
|
|
#include <linux/ratelimit.h>
|
|
#include <linux/irq.h>
|
|
|
|
#include "internals.h"
|
|
|
|
static bool migrate_one_irq(struct irq_desc *desc)
|
|
{
|
|
struct irq_data *d = irq_desc_get_irq_data(desc);
|
|
struct irq_chip *chip = irq_data_get_irq_chip(d);
|
|
bool maskchip = !irq_can_move_pcntxt(d) && !irqd_irq_masked(d);
|
|
const struct cpumask *affinity;
|
|
bool brokeaff = false;
|
|
int err;
|
|
|
|
/*
|
|
* IRQ chip might be already torn down, but the irq descriptor is
|
|
* still in the radix tree. Also if the chip has no affinity setter,
|
|
* nothing can be done here.
|
|
*/
|
|
if (!chip || !chip->irq_set_affinity) {
|
|
pr_debug("IRQ %u: Unable to migrate away\n", d->irq);
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
* No move required, if:
|
|
* - Interrupt is per cpu
|
|
* - Interrupt is not started
|
|
* - Affinity mask does not include this CPU.
|
|
*
|
|
* Note: Do not check desc->action as this might be a chained
|
|
* interrupt.
|
|
*/
|
|
affinity = irq_data_get_affinity_mask(d);
|
|
if (irqd_is_per_cpu(d) || !irqd_is_started(d) ||
|
|
!cpumask_test_cpu(smp_processor_id(), affinity)) {
|
|
/*
|
|
* If an irq move is pending, abort it if the dying CPU is
|
|
* the sole target.
|
|
*/
|
|
irq_fixup_move_pending(desc, false);
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
* Complete an eventually pending irq move cleanup. If this
|
|
* interrupt was moved in hard irq context, then the vectors need
|
|
* to be cleaned up. It can't wait until this interrupt actually
|
|
* happens and this CPU was involved.
|
|
*/
|
|
irq_force_complete_move(desc);
|
|
|
|
/*
|
|
* If there is a setaffinity pending, then try to reuse the pending
|
|
* mask, so the last change of the affinity does not get lost. If
|
|
* there is no move pending or the pending mask does not contain
|
|
* any online CPU, use the current affinity mask.
|
|
*/
|
|
if (irq_fixup_move_pending(desc, true))
|
|
affinity = irq_desc_get_pending_mask(desc);
|
|
|
|
/* Mask the chip for interrupts which cannot move in process context */
|
|
if (maskchip && chip->irq_mask)
|
|
chip->irq_mask(d);
|
|
|
|
if (cpumask_any_and(affinity, cpu_online_mask) >= nr_cpu_ids) {
|
|
affinity = cpu_online_mask;
|
|
brokeaff = true;
|
|
}
|
|
|
|
err = irq_do_set_affinity(d, affinity, false);
|
|
if (err) {
|
|
pr_warn_ratelimited("IRQ%u: set affinity failed(%d).\n",
|
|
d->irq, err);
|
|
brokeaff = false;
|
|
}
|
|
|
|
if (maskchip && chip->irq_unmask)
|
|
chip->irq_unmask(d);
|
|
|
|
return brokeaff;
|
|
}
|
|
|
|
/**
|
|
* irq_migrate_all_off_this_cpu - Migrate irqs away from offline cpu
|
|
*
|
|
* The current CPU has been marked offline. Migrate IRQs off this CPU.
|
|
* If the affinity settings do not allow other CPUs, force them onto any
|
|
* available CPU.
|
|
*
|
|
* Note: we must iterate over all IRQs, whether they have an attached
|
|
* action structure or not, as we need to get chained interrupts too.
|
|
*/
|
|
void irq_migrate_all_off_this_cpu(void)
|
|
{
|
|
struct irq_desc *desc;
|
|
unsigned int irq;
|
|
|
|
for_each_active_irq(irq) {
|
|
bool affinity_broken;
|
|
|
|
desc = irq_to_desc(irq);
|
|
raw_spin_lock(&desc->lock);
|
|
affinity_broken = migrate_one_irq(desc);
|
|
raw_spin_unlock(&desc->lock);
|
|
|
|
if (affinity_broken) {
|
|
pr_warn_ratelimited("IRQ %u: no longer affine to CPU%u\n",
|
|
irq, smp_processor_id());
|
|
}
|
|
}
|
|
}
|