Skip to content

Commit 01f8fa4

Browse files
committed
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 <[email protected]> Signed-off-by: Thomas Gleixner <[email protected]> Cc: Kyungmin Park <[email protected]> Cc: Marek Szyprowski <[email protected]> Cc: Bartlomiej Zolnierkiewicz <[email protected]> Cc: Tomasz Figa <[email protected]>, Cc: Daniel Lezcano <[email protected]>, Cc: Kukjin Kim <[email protected]> Cc: [email protected], Cc: [email protected] Link: http://lkml.kernel.org/r/[email protected] Signed-off-by: Thomas Gleixner <[email protected]>
1 parent 81cef0f commit 01f8fa4

File tree

4 files changed

+43
-14
lines changed

4 files changed

+43
-14
lines changed

arch/mips/cavium-octeon/octeon-irq.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -635,7 +635,7 @@ static void octeon_irq_cpu_offline_ciu(struct irq_data *data)
635635
cpumask_clear(&new_affinity);
636636
cpumask_set_cpu(cpumask_first(cpu_online_mask), &new_affinity);
637637
}
638-
__irq_set_affinity_locked(data, &new_affinity);
638+
irq_set_affinity_locked(data, &new_affinity, false);
639639
}
640640

641641
static int octeon_irq_ciu_set_affinity(struct irq_data *data,

include/linux/interrupt.h

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,40 @@ static inline int check_wakeup_irqs(void) { return 0; }
203203

204204
extern cpumask_var_t irq_default_affinity;
205205

206-
extern int irq_set_affinity(unsigned int irq, const struct cpumask *cpumask);
206+
/* Internal implementation. Use the helpers below */
207+
extern 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+
*/
217+
static inline int
218+
irq_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+
*/
234+
static inline int
235+
irq_force_affinity(unsigned int irq, const struct cpumask *cpumask)
236+
{
237+
return __irq_set_affinity(irq, cpumask, true);
238+
}
239+
207240
extern int irq_can_set_affinity(unsigned int irq);
208241
extern int irq_select_affinity(unsigned int irq);
209242

include/linux/irq.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -394,7 +394,8 @@ extern void remove_percpu_irq(unsigned int irq, struct irqaction *act);
394394

395395
extern void irq_cpu_online(void);
396396
extern void irq_cpu_offline(void);
397-
extern int __irq_set_affinity_locked(struct irq_data *data, const struct cpumask *cpumask);
397+
extern int irq_set_affinity_locked(struct irq_data *data,
398+
const struct cpumask *cpumask, bool force);
398399

399400
#if defined(CONFIG_SMP) && defined(CONFIG_GENERIC_PENDING_IRQ)
400401
void irq_move_irq(struct irq_data *data);

kernel/irq/manage.c

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ int irq_do_set_affinity(struct irq_data *data, const struct cpumask *mask,
180180
struct irq_chip *chip = irq_data_get_irq_chip(data);
181181
int ret;
182182

183-
ret = chip->irq_set_affinity(data, mask, false);
183+
ret = chip->irq_set_affinity(data, mask, force);
184184
switch (ret) {
185185
case IRQ_SET_MASK_OK:
186186
cpumask_copy(data->affinity, mask);
@@ -192,7 +192,8 @@ int irq_do_set_affinity(struct irq_data *data, const struct cpumask *mask,
192192
return ret;
193193
}
194194

195-
int __irq_set_affinity_locked(struct irq_data *data, const struct cpumask *mask)
195+
int irq_set_affinity_locked(struct irq_data *data, const struct cpumask *mask,
196+
bool force)
196197
{
197198
struct irq_chip *chip = irq_data_get_irq_chip(data);
198199
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)
202203
return -EINVAL;
203204

204205
if (irq_can_move_pcntxt(data)) {
205-
ret = irq_do_set_affinity(data, mask, false);
206+
ret = irq_do_set_affinity(data, mask, force);
206207
} else {
207208
irqd_set_move_pending(data);
208209
irq_copy_pending(desc, mask);
@@ -217,13 +218,7 @@ int __irq_set_affinity_locked(struct irq_data *data, const struct cpumask *mask)
217218
return ret;
218219
}
219220

220-
/**
221-
* irq_set_affinity - Set the irq affinity of a given irq
222-
* @irq: Interrupt to set affinity
223-
* @mask: cpumask
224-
*
225-
*/
226-
int irq_set_affinity(unsigned int irq, const struct cpumask *mask)
221+
int __irq_set_affinity(unsigned int irq, const struct cpumask *mask, bool force)
227222
{
228223
struct irq_desc *desc = irq_to_desc(irq);
229224
unsigned long flags;
@@ -233,7 +228,7 @@ int irq_set_affinity(unsigned int irq, const struct cpumask *mask)
233228
return -EINVAL;
234229

235230
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);
237232
raw_spin_unlock_irqrestore(&desc->lock, flags);
238233
return ret;
239234
}

0 commit comments

Comments
 (0)