| From 01f8fa4f01d8362358eb90e412bd7ae18a3ec1ad Mon Sep 17 00:00:00 2001 |
| From: Thomas Gleixner <tglx@linutronix.de> |
| Date: Wed, 16 Apr 2014 14:36:44 +0000 |
| Subject: genirq: Allow forcing cpu affinity of interrupts |
| |
| From: Thomas Gleixner <tglx@linutronix.de> |
| |
| commit 01f8fa4f01d8362358eb90e412bd7ae18a3ec1ad upstream. |
| |
| The current implementation of irq_set_affinity() refuses rightfully to |
| route an interrupt to an offline cpu. |
| |
| But there is a special case, where this is actually desired. Some of |
| the ARM SoCs have per cpu timers which require setting the affinity |
| during cpu startup where the cpu is not yet in the online mask. |
| |
| If we can't do that, then the local timer interrupt for the about to |
| become online cpu is routed to some random online cpu. |
| |
| The developers of the affected machines tried to work around that |
| issue, but that results in a massive mess in that timer code. |
| |
| We have a yet unused argument in the set_affinity callbacks of the irq |
| chips, which I added back then for a similar reason. It was never |
| required so it got not used. But I'm happy that I never removed it. |
| |
| That allows us to implement a sane handling of the above scenario. So |
| the affected SoC drivers can add the required force handling to their |
| interrupt chip, switch the timer code to irq_force_affinity() and |
| things just work. |
| |
| This does not affect any existing user of irq_set_affinity(). |
| |
| Tagged for stable to allow a simple fix of the affected SoC clock |
| event drivers. |
| |
| Reported-and-tested-by: Krzysztof Kozlowski <k.kozlowski@samsung.com> |
| Signed-off-by: Thomas Gleixner <tglx@linutronix.de> |
| Cc: Kyungmin Park <kyungmin.park@samsung.com> |
| Cc: Marek Szyprowski <m.szyprowski@samsung.com> |
| Cc: Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com> |
| Cc: Tomasz Figa <t.figa@samsung.com>, |
| Cc: Daniel Lezcano <daniel.lezcano@linaro.org>, |
| Cc: Kukjin Kim <kgene.kim@samsung.com> |
| Cc: linux-arm-kernel@lists.infradead.org, |
| Link: http://lkml.kernel.org/r/20140416143315.717251504@linutronix.de |
| Signed-off-by: Thomas Gleixner <tglx@linutronix.de> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| arch/mips/cavium-octeon/octeon-irq.c | 2 +- |
| include/linux/interrupt.h | 35 ++++++++++++++++++++++++++++++++++- |
| include/linux/irq.h | 3 ++- |
| kernel/irq/manage.c | 17 ++++++----------- |
| 4 files changed, 43 insertions(+), 14 deletions(-) |
| |
| --- a/arch/mips/cavium-octeon/octeon-irq.c |
| +++ b/arch/mips/cavium-octeon/octeon-irq.c |
| @@ -635,7 +635,7 @@ static void octeon_irq_cpu_offline_ciu(s |
| cpumask_clear(&new_affinity); |
| cpumask_set_cpu(cpumask_first(cpu_online_mask), &new_affinity); |
| } |
| - __irq_set_affinity_locked(data, &new_affinity); |
| + irq_set_affinity_locked(data, &new_affinity, false); |
| } |
| |
| static int octeon_irq_ciu_set_affinity(struct irq_data *data, |
| --- a/include/linux/interrupt.h |
| +++ b/include/linux/interrupt.h |
| @@ -239,7 +239,40 @@ static inline int check_wakeup_irqs(void |
| |
| extern cpumask_var_t irq_default_affinity; |
| |
| -extern int irq_set_affinity(unsigned int irq, const struct cpumask *cpumask); |
| +/* Internal implementation. Use the helpers below */ |
| +extern int __irq_set_affinity(unsigned int irq, const struct cpumask *cpumask, |
| + bool force); |
| + |
| +/** |
| + * irq_set_affinity - Set the irq affinity of a given irq |
| + * @irq: Interrupt to set affinity |
| + * @mask: cpumask |
| + * |
| + * Fails if cpumask does not contain an online CPU |
| + */ |
| +static inline int |
| +irq_set_affinity(unsigned int irq, const struct cpumask *cpumask) |
| +{ |
| + return __irq_set_affinity(irq, cpumask, false); |
| +} |
| + |
| +/** |
| + * irq_force_affinity - Force the irq affinity of a given irq |
| + * @irq: Interrupt to set affinity |
| + * @mask: cpumask |
| + * |
| + * Same as irq_set_affinity, but without checking the mask against |
| + * online cpus. |
| + * |
| + * Solely for low level cpu hotplug code, where we need to make per |
| + * cpu interrupts affine before the cpu becomes online. |
| + */ |
| +static inline int |
| +irq_force_affinity(unsigned int irq, const struct cpumask *cpumask) |
| +{ |
| + return __irq_set_affinity(irq, cpumask, true); |
| +} |
| + |
| extern int irq_can_set_affinity(unsigned int irq); |
| extern int irq_select_affinity(unsigned int irq); |
| |
| --- a/include/linux/irq.h |
| +++ b/include/linux/irq.h |
| @@ -375,7 +375,8 @@ extern void remove_percpu_irq(unsigned i |
| |
| extern void irq_cpu_online(void); |
| extern void irq_cpu_offline(void); |
| -extern int __irq_set_affinity_locked(struct irq_data *data, const struct cpumask *cpumask); |
| +extern int irq_set_affinity_locked(struct irq_data *data, |
| + const struct cpumask *cpumask, bool force); |
| |
| #ifdef CONFIG_GENERIC_HARDIRQS |
| |
| --- a/kernel/irq/manage.c |
| +++ b/kernel/irq/manage.c |
| @@ -150,7 +150,7 @@ int irq_do_set_affinity(struct irq_data |
| struct irq_chip *chip = irq_data_get_irq_chip(data); |
| int ret; |
| |
| - ret = chip->irq_set_affinity(data, mask, false); |
| + ret = chip->irq_set_affinity(data, mask, force); |
| switch (ret) { |
| case IRQ_SET_MASK_OK: |
| cpumask_copy(data->affinity, mask); |
| @@ -162,7 +162,8 @@ int irq_do_set_affinity(struct irq_data |
| return ret; |
| } |
| |
| -int __irq_set_affinity_locked(struct irq_data *data, const struct cpumask *mask) |
| +int irq_set_affinity_locked(struct irq_data *data, const struct cpumask *mask, |
| + bool force) |
| { |
| struct irq_chip *chip = irq_data_get_irq_chip(data); |
| struct irq_desc *desc = irq_data_to_desc(data); |
| @@ -172,7 +173,7 @@ int __irq_set_affinity_locked(struct irq |
| return -EINVAL; |
| |
| if (irq_can_move_pcntxt(data)) { |
| - ret = irq_do_set_affinity(data, mask, false); |
| + ret = irq_do_set_affinity(data, mask, force); |
| } else { |
| irqd_set_move_pending(data); |
| irq_copy_pending(desc, mask); |
| @@ -187,13 +188,7 @@ int __irq_set_affinity_locked(struct irq |
| return ret; |
| } |
| |
| -/** |
| - * irq_set_affinity - Set the irq affinity of a given irq |
| - * @irq: Interrupt to set affinity |
| - * @mask: cpumask |
| - * |
| - */ |
| -int irq_set_affinity(unsigned int irq, const struct cpumask *mask) |
| +int __irq_set_affinity(unsigned int irq, const struct cpumask *mask, bool force) |
| { |
| struct irq_desc *desc = irq_to_desc(irq); |
| unsigned long flags; |
| @@ -203,7 +198,7 @@ int irq_set_affinity(unsigned int irq, c |
| return -EINVAL; |
| |
| raw_spin_lock_irqsave(&desc->lock, flags); |
| - ret = __irq_set_affinity_locked(irq_desc_get_irq_data(desc), mask); |
| + ret = irq_set_affinity_locked(irq_desc_get_irq_data(desc), mask, force); |
| raw_spin_unlock_irqrestore(&desc->lock, flags); |
| return ret; |
| } |