aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas Gleixner <tglx@linutronix.de>2014-04-16 10:36:44 -0400
committerThomas Gleixner <tglx@linutronix.de>2014-04-17 17:36:27 -0400
commit01f8fa4f01d8362358eb90e412bd7ae18a3ec1ad (patch)
treec6b5f7c2ae3e1488d900907ae8369967469af8d5
parent81cef0fe19e086ff6abfd45e92246f68ffa0185f (diff)
genirq: Allow forcing cpu affinity of interrupts
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, Cc: stable@vger.kernel.org Link: http://lkml.kernel.org/r/20140416143315.717251504@linutronix.de Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
-rw-r--r--arch/mips/cavium-octeon/octeon-irq.c2
-rw-r--r--include/linux/interrupt.h35
-rw-r--r--include/linux/irq.h3
-rw-r--r--kernel/irq/manage.c17
4 files changed, 43 insertions, 14 deletions
diff --git a/arch/mips/cavium-octeon/octeon-irq.c b/arch/mips/cavium-octeon/octeon-irq.c
index c2bb4f896ce7..3aa5b46b2d40 100644
--- 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(struct irq_data *data)
635 cpumask_clear(&new_affinity); 635 cpumask_clear(&new_affinity);
636 cpumask_set_cpu(cpumask_first(cpu_online_mask), &new_affinity); 636 cpumask_set_cpu(cpumask_first(cpu_online_mask), &new_affinity);
637 } 637 }
638 __irq_set_affinity_locked(data, &new_affinity); 638 irq_set_affinity_locked(data, &new_affinity, false);
639} 639}
640 640
641static int octeon_irq_ciu_set_affinity(struct irq_data *data, 641static int octeon_irq_ciu_set_affinity(struct irq_data *data,
diff --git a/include/linux/interrupt.h b/include/linux/interrupt.h
index c7bfac1c4a7b..8834a7e5b944 100644
--- a/include/linux/interrupt.h
+++ b/include/linux/interrupt.h
@@ -203,7 +203,40 @@ static inline int check_wakeup_irqs(void) { return 0; }
203 203
204extern cpumask_var_t irq_default_affinity; 204extern cpumask_var_t irq_default_affinity;
205 205
206extern int irq_set_affinity(unsigned int irq, const struct cpumask *cpumask); 206/* Internal implementation. Use the helpers below */
207extern int __irq_set_affinity(unsigned int irq, const struct cpumask *cpumask,
208 bool force);
209
210/**
211 * irq_set_affinity - Set the irq affinity of a given irq
212 * @irq: Interrupt to set affinity
213 * @mask: cpumask
214 *
215 * Fails if cpumask does not contain an online CPU
216 */
217static inline int
218irq_set_affinity(unsigned int irq, const struct cpumask *cpumask)
219{
220 return __irq_set_affinity(irq, cpumask, false);
221}
222
223/**
224 * irq_force_affinity - Force the irq affinity of a given irq
225 * @irq: Interrupt to set affinity
226 * @mask: cpumask
227 *
228 * Same as irq_set_affinity, but without checking the mask against
229 * online cpus.
230 *
231 * Solely for low level cpu hotplug code, where we need to make per
232 * cpu interrupts affine before the cpu becomes online.
233 */
234static inline int
235irq_force_affinity(unsigned int irq, const struct cpumask *cpumask)
236{
237 return __irq_set_affinity(irq, cpumask, true);
238}
239
207extern int irq_can_set_affinity(unsigned int irq); 240extern int irq_can_set_affinity(unsigned int irq);
208extern int irq_select_affinity(unsigned int irq); 241extern int irq_select_affinity(unsigned int irq);
209 242
diff --git a/include/linux/irq.h b/include/linux/irq.h
index d278838908cb..10a0b1ac4ea0 100644
--- a/include/linux/irq.h
+++ b/include/linux/irq.h
@@ -394,7 +394,8 @@ extern void remove_percpu_irq(unsigned int irq, struct irqaction *act);
394 394
395extern void irq_cpu_online(void); 395extern void irq_cpu_online(void);
396extern void irq_cpu_offline(void); 396extern void irq_cpu_offline(void);
397extern int __irq_set_affinity_locked(struct irq_data *data, const struct cpumask *cpumask); 397extern int irq_set_affinity_locked(struct irq_data *data,
398 const struct cpumask *cpumask, bool force);
398 399
399#if defined(CONFIG_SMP) && defined(CONFIG_GENERIC_PENDING_IRQ) 400#if defined(CONFIG_SMP) && defined(CONFIG_GENERIC_PENDING_IRQ)
400void irq_move_irq(struct irq_data *data); 401void irq_move_irq(struct irq_data *data);
diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c
index 2486a4c1a710..d34131ca372b 100644
--- a/kernel/irq/manage.c
+++ b/kernel/irq/manage.c
@@ -180,7 +180,7 @@ int irq_do_set_affinity(struct irq_data *data, const struct cpumask *mask,
180 struct irq_chip *chip = irq_data_get_irq_chip(data); 180 struct irq_chip *chip = irq_data_get_irq_chip(data);
181 int ret; 181 int ret;
182 182
183 ret = chip->irq_set_affinity(data, mask, false); 183 ret = chip->irq_set_affinity(data, mask, force);
184 switch (ret) { 184 switch (ret) {
185 case IRQ_SET_MASK_OK: 185 case IRQ_SET_MASK_OK:
186 cpumask_copy(data->affinity, mask); 186 cpumask_copy(data->affinity, mask);
@@ -192,7 +192,8 @@ int irq_do_set_affinity(struct irq_data *data, const struct cpumask *mask,
192 return ret; 192 return ret;
193} 193}
194 194
195int __irq_set_affinity_locked(struct irq_data *data, const struct cpumask *mask) 195int irq_set_affinity_locked(struct irq_data *data, const struct cpumask *mask,
196 bool force)
196{ 197{
197 struct irq_chip *chip = irq_data_get_irq_chip(data); 198 struct irq_chip *chip = irq_data_get_irq_chip(data);
198 struct irq_desc *desc = irq_data_to_desc(data); 199 struct irq_desc *desc = irq_data_to_desc(data);
@@ -202,7 +203,7 @@ int __irq_set_affinity_locked(struct irq_data *data, const struct cpumask *mask)
202 return -EINVAL; 203 return -EINVAL;
203 204
204 if (irq_can_move_pcntxt(data)) { 205 if (irq_can_move_pcntxt(data)) {
205 ret = irq_do_set_affinity(data, mask, false); 206 ret = irq_do_set_affinity(data, mask, force);
206 } else { 207 } else {
207 irqd_set_move_pending(data); 208 irqd_set_move_pending(data);
208 irq_copy_pending(desc, mask); 209 irq_copy_pending(desc, mask);
@@ -217,13 +218,7 @@ int __irq_set_affinity_locked(struct irq_data *data, const struct cpumask *mask)
217 return ret; 218 return ret;
218} 219}
219 220
220/** 221int __irq_set_affinity(unsigned int irq, const struct cpumask *mask, bool force)
221 * irq_set_affinity - Set the irq affinity of a given irq
222 * @irq: Interrupt to set affinity
223 * @mask: cpumask
224 *
225 */
226int irq_set_affinity(unsigned int irq, const struct cpumask *mask)
227{ 222{
228 struct irq_desc *desc = irq_to_desc(irq); 223 struct irq_desc *desc = irq_to_desc(irq);
229 unsigned long flags; 224 unsigned long flags;
@@ -233,7 +228,7 @@ int irq_set_affinity(unsigned int irq, const struct cpumask *mask)
233 return -EINVAL; 228 return -EINVAL;
234 229
235 raw_spin_lock_irqsave(&desc->lock, flags); 230 raw_spin_lock_irqsave(&desc->lock, flags);
236 ret = __irq_set_affinity_locked(irq_desc_get_irq_data(desc), mask); 231 ret = irq_set_affinity_locked(irq_desc_get_irq_data(desc), mask, force);
237 raw_spin_unlock_irqrestore(&desc->lock, flags); 232 raw_spin_unlock_irqrestore(&desc->lock, flags);
238 return ret; 233 return ret;
239} 234}