diff options
Diffstat (limited to 'arch/arm/kernel/irq.c')
| -rw-r--r-- | arch/arm/kernel/irq.c | 129 |
1 files changed, 126 insertions, 3 deletions
diff --git a/arch/arm/kernel/irq.c b/arch/arm/kernel/irq.c index 5b35e76c3ce4..5e0656e88918 100644 --- a/arch/arm/kernel/irq.c +++ b/arch/arm/kernel/irq.c @@ -32,6 +32,7 @@ #include <linux/errno.h> #include <linux/list.h> #include <linux/kallsyms.h> +#include <linux/proc_fs.h> #include <asm/irq.h> #include <asm/system.h> @@ -85,6 +86,23 @@ static struct irqdesc bad_irq_desc = { .disable_depth = 1, }; +#ifdef CONFIG_SMP +void synchronize_irq(unsigned int irq) +{ + struct irqdesc *desc = irq_desc + irq; + + while (desc->running) + barrier(); +} +EXPORT_SYMBOL(synchronize_irq); + +#define smp_set_running(desc) do { desc->running = 1; } while (0) +#define smp_clear_running(desc) do { desc->running = 0; } while (0) +#else +#define smp_set_running(desc) do { } while (0) +#define smp_clear_running(desc) do { } while (0) +#endif + /** * disable_irq_nosync - disable an irq without waiting * @irq: Interrupt to disable @@ -232,6 +250,9 @@ unlock: #ifdef CONFIG_ARCH_ACORN show_fiq_list(p, v); #endif +#ifdef CONFIG_SMP + show_ipi_list(p); +#endif seq_printf(p, "Err: %10lu\n", irq_err_count); } return 0; @@ -329,18 +350,22 @@ void do_simple_IRQ(unsigned int irq, struct irqdesc *desc, struct pt_regs *regs) { struct irqaction *action; - const int cpu = smp_processor_id(); + const unsigned int cpu = smp_processor_id(); desc->triggered = 1; kstat_cpu(cpu).irqs[irq]++; + smp_set_running(desc); + action = desc->action; if (action) { int ret = __do_irq(irq, action, regs); if (ret != IRQ_HANDLED) report_bad_irq(irq, regs, desc, ret); } + + smp_clear_running(desc); } /* @@ -350,7 +375,7 @@ do_simple_IRQ(unsigned int irq, struct irqdesc *desc, struct pt_regs *regs) void do_edge_IRQ(unsigned int irq, struct irqdesc *desc, struct pt_regs *regs) { - const int cpu = smp_processor_id(); + const unsigned int cpu = smp_processor_id(); desc->triggered = 1; @@ -414,7 +439,7 @@ void do_level_IRQ(unsigned int irq, struct irqdesc *desc, struct pt_regs *regs) { struct irqaction *action; - const int cpu = smp_processor_id(); + const unsigned int cpu = smp_processor_id(); desc->triggered = 1; @@ -426,6 +451,8 @@ do_level_IRQ(unsigned int irq, struct irqdesc *desc, struct pt_regs *regs) if (likely(!desc->disable_depth)) { kstat_cpu(cpu).irqs[irq]++; + smp_set_running(desc); + /* * Return with this interrupt masked if no action */ @@ -440,6 +467,8 @@ do_level_IRQ(unsigned int irq, struct irqdesc *desc, struct pt_regs *regs) !check_irq_lock(desc, irq, regs))) desc->chip->unmask(irq); } + + smp_clear_running(desc); } } @@ -878,8 +907,97 @@ out: EXPORT_SYMBOL(probe_irq_off); +#ifdef CONFIG_SMP +static void route_irq(struct irqdesc *desc, unsigned int irq, unsigned int cpu) +{ + pr_debug("IRQ%u: moving from cpu%u to cpu%u\n", irq, desc->cpu, cpu); + + spin_lock_irq(&irq_controller_lock); + desc->cpu = cpu; + desc->chip->set_cpu(desc, irq, cpu); + spin_unlock_irq(&irq_controller_lock); +} + +#ifdef CONFIG_PROC_FS +static int +irq_affinity_read_proc(char *page, char **start, off_t off, int count, + int *eof, void *data) +{ + struct irqdesc *desc = irq_desc + ((int)data); + int len = cpumask_scnprintf(page, count, desc->affinity); + + if (count - len < 2) + return -EINVAL; + page[len++] = '\n'; + page[len] = '\0'; + + return len; +} + +static int +irq_affinity_write_proc(struct file *file, const char __user *buffer, + unsigned long count, void *data) +{ + unsigned int irq = (unsigned int)data; + struct irqdesc *desc = irq_desc + irq; + cpumask_t affinity, tmp; + int ret = -EIO; + + if (!desc->chip->set_cpu) + goto out; + + ret = cpumask_parse(buffer, count, affinity); + if (ret) + goto out; + + cpus_and(tmp, affinity, cpu_online_map); + if (cpus_empty(tmp)) { + ret = -EINVAL; + goto out; + } + + desc->affinity = affinity; + route_irq(desc, irq, first_cpu(tmp)); + ret = count; + + out: + return ret; +} +#endif +#endif + void __init init_irq_proc(void) { +#if defined(CONFIG_SMP) && defined(CONFIG_PROC_FS) + struct proc_dir_entry *dir; + int irq; + + dir = proc_mkdir("irq", 0); + if (!dir) + return; + + for (irq = 0; irq < NR_IRQS; irq++) { + struct proc_dir_entry *entry; + struct irqdesc *desc; + char name[16]; + + desc = irq_desc + irq; + memset(name, 0, sizeof(name)); + snprintf(name, sizeof(name) - 1, "%u", irq); + + desc->procdir = proc_mkdir(name, dir); + if (!desc->procdir) + continue; + + entry = create_proc_entry("smp_affinity", 0600, desc->procdir); + if (entry) { + entry->nlink = 1; + entry->data = (void *)irq; + entry->read_proc = irq_affinity_read_proc; + entry->write_proc = irq_affinity_write_proc; + } + } +#endif } void __init init_IRQ(void) @@ -888,6 +1006,11 @@ void __init init_IRQ(void) extern void init_dma(void); int irq; +#ifdef CONFIG_SMP + bad_irq_desc.affinity = CPU_MASK_ALL; + bad_irq_desc.cpu = smp_processor_id(); +#endif + for (irq = 0, desc = irq_desc; irq < NR_IRQS; irq++, desc++) { *desc = bad_irq_desc; INIT_LIST_HEAD(&desc->pend); |
