X-Git-Url: http://ftp.safe.ca/?a=blobdiff_plain;f=kernel%2Firq%2Fhandle.c;h=5dd2572993cf16b3132cda6cc215d5fde96d3b99;hb=44347d947f628060b92449702071bfe1d31dfb75;hp=1f346990f3f89983588d2836d387f863798e7e3e;hpb=7f95ec9e4c12fd067febfd57532da1166d75d858;p=safe%2Fjmp%2Flinux-2.6 diff --git a/kernel/irq/handle.c b/kernel/irq/handle.c index 1f34699..5dd2572 100644 --- a/kernel/irq/handle.c +++ b/kernel/irq/handle.c @@ -15,16 +15,17 @@ #include #include #include +#include +#include +#include +#include #include "internals.h" -#ifdef CONFIG_TRACE_IRQFLAGS - /* * lockdep: we want to handle all irq_desc locks as a single lock-class: */ -static struct lock_class_key irq_desc_lock_class; -#endif +struct lock_class_key irq_desc_lock_class; /** * handle_bad_irq - handle spurious and unhandled irqs @@ -33,14 +34,25 @@ static struct lock_class_key irq_desc_lock_class; * * Handles spurious and unhandled IRQ's. It also prints a debugmessage. */ -void -handle_bad_irq(unsigned int irq, struct irq_desc *desc) +void handle_bad_irq(unsigned int irq, struct irq_desc *desc) { print_irq_desc(irq, desc); - kstat_irqs_this_cpu(desc)++; + kstat_incr_irqs_this_cpu(irq, desc); ack_bad_irq(irq); } +#if defined(CONFIG_SMP) && defined(CONFIG_GENERIC_HARDIRQS) +static void __init init_irq_default_affinity(void) +{ + alloc_bootmem_cpumask_var(&irq_default_affinity); + cpumask_setall(irq_default_affinity); +} +#else +static void __init init_irq_default_affinity(void) +{ +} +#endif + /* * Linux has a controller-independent interrupt architecture. * Every controller has a 'controller-template', that is used @@ -58,163 +70,165 @@ handle_bad_irq(unsigned int irq, struct irq_desc *desc) int nr_irqs = NR_IRQS; EXPORT_SYMBOL_GPL(nr_irqs); -#ifdef CONFIG_HAVE_DYN_ARRAY +#ifdef CONFIG_SPARSE_IRQ + static struct irq_desc irq_desc_init = { - .irq = -1U, - .status = IRQ_DISABLED, - .chip = &no_irq_chip, + .irq = -1, + .status = IRQ_DISABLED, + .chip = &no_irq_chip, .handle_irq = handle_bad_irq, - .depth = 1, - .lock = __SPIN_LOCK_UNLOCKED(irq_desc_init.lock), -#ifdef CONFIG_SMP - .affinity = CPU_MASK_ALL -#endif + .depth = 1, + .lock = __SPIN_LOCK_UNLOCKED(irq_desc_init.lock), }; - -static void init_one_irq_desc(struct irq_desc *desc) +void init_kstat_irqs(struct irq_desc *desc, int cpu, int nr) { - memcpy(desc, &irq_desc_init, sizeof(struct irq_desc)); -#ifdef CONFIG_TRACE_IRQFLAGS - lockdep_set_class(&desc->lock, &irq_desc_lock_class); -#endif -} + int node; + void *ptr; -extern int after_bootmem; -extern void *__alloc_bootmem_nopanic(unsigned long size, - unsigned long align, - unsigned long goal); + node = cpu_to_node(cpu); + ptr = kzalloc_node(nr * sizeof(*desc->kstat_irqs), GFP_ATOMIC, node); -static void init_kstat_irqs(struct irq_desc *desc, int nr_desc, int nr) + /* + * don't overwite if can not get new one + * init_copy_kstat_irqs() could still use old one + */ + if (ptr) { + printk(KERN_DEBUG " alloc kstat_irqs on cpu %d node %d\n", + cpu, node); + desc->kstat_irqs = ptr; + } +} + +static void init_one_irq_desc(int irq, struct irq_desc *desc, int cpu) { - unsigned long bytes, total_bytes; - char *ptr; - int i; - unsigned long phys; + memcpy(desc, &irq_desc_init, sizeof(struct irq_desc)); - /* Compute how many bytes we need per irq and allocate them */ - bytes = nr * sizeof(unsigned int); - total_bytes = bytes * nr_desc; - if (after_bootmem) - ptr = kzalloc(total_bytes, GFP_ATOMIC); - else - ptr = __alloc_bootmem_nopanic(total_bytes, PAGE_SIZE, 0); + spin_lock_init(&desc->lock); + desc->irq = irq; +#ifdef CONFIG_SMP + desc->cpu = cpu; +#endif + lockdep_set_class(&desc->lock, &irq_desc_lock_class); + init_kstat_irqs(desc, cpu, nr_cpu_ids); + if (!desc->kstat_irqs) { + printk(KERN_ERR "can not alloc kstat_irqs\n"); + BUG_ON(1); + } + if (!init_alloc_desc_masks(desc, cpu, false)) { + printk(KERN_ERR "can not alloc irq_desc cpumasks\n"); + BUG_ON(1); + } + arch_init_chip_data(desc, cpu); +} - if (!ptr) - panic(" can not allocate kstat_irqs\n"); +/* + * Protect the sparse_irqs: + */ +DEFINE_SPINLOCK(sparse_irq_lock); - phys = __pa(ptr); - printk(KERN_DEBUG "kstat_irqs ==> [%#lx - %#lx]\n", phys, phys + total_bytes); +struct irq_desc **irq_desc_ptrs __read_mostly; - for (i = 0; i < nr_desc; i++) { - desc[i].kstat_irqs = (unsigned int *)ptr; - ptr += bytes; +static struct irq_desc irq_desc_legacy[NR_IRQS_LEGACY] __cacheline_aligned_in_smp = { + [0 ... NR_IRQS_LEGACY-1] = { + .irq = -1, + .status = IRQ_DISABLED, + .chip = &no_irq_chip, + .handle_irq = handle_bad_irq, + .depth = 1, + .lock = __SPIN_LOCK_UNLOCKED(irq_desc_init.lock), } -} +}; +static unsigned int *kstat_irqs_legacy; -static void __init init_work(void *data) +int __init early_irq_init(void) { - struct dyn_array *da = data; + struct irq_desc *desc; + int legacy_count; int i; - struct irq_desc *desc; - desc = *da->name; + init_irq_default_affinity(); - for (i = 0; i < *da->nr; i++) { - init_one_irq_desc(&desc[i]); -#ifndef CONFIG_HAVE_SPARSE_IRQ - desc[i].irq = i; -#endif - } + /* initialize nr_irqs based on nr_cpu_ids */ + arch_probe_nr_irqs(); + printk(KERN_INFO "NR_IRQS:%d nr_irqs:%d\n", NR_IRQS, nr_irqs); -#ifdef CONFIG_HAVE_SPARSE_IRQ - for (i = 1; i < *da->nr; i++) - desc[i-1].next = &desc[i]; -#endif + desc = irq_desc_legacy; + legacy_count = ARRAY_SIZE(irq_desc_legacy); - /* init kstat_irqs, nr_cpu_ids is ready already */ - init_kstat_irqs(desc, *da->nr, nr_cpu_ids); -} + /* allocate irq_desc_ptrs array based on nr_irqs */ + irq_desc_ptrs = alloc_bootmem(nr_irqs * sizeof(void *)); -#ifdef CONFIG_HAVE_SPARSE_IRQ -static int nr_irq_desc = 32; + /* allocate based on nr_cpu_ids */ + /* FIXME: invert kstat_irgs, and it'd be a per_cpu_alloc'd thing */ + kstat_irqs_legacy = alloc_bootmem(NR_IRQS_LEGACY * nr_cpu_ids * + sizeof(int)); -static int __init parse_nr_irq_desc(char *arg) -{ - if (arg) - nr_irq_desc = simple_strtoul(arg, NULL, 0); - return 0; -} + for (i = 0; i < legacy_count; i++) { + desc[i].irq = i; + desc[i].kstat_irqs = kstat_irqs_legacy + i * nr_cpu_ids; + lockdep_set_class(&desc[i].lock, &irq_desc_lock_class); + init_alloc_desc_masks(&desc[i], 0, true); + irq_desc_ptrs[i] = desc + i; + } -early_param("nr_irq_desc", parse_nr_irq_desc); + for (i = legacy_count; i < nr_irqs; i++) + irq_desc_ptrs[i] = NULL; -static struct irq_desc *sparse_irqs; -DEFINE_DYN_ARRAY(sparse_irqs, sizeof(struct irq_desc), nr_irq_desc, PAGE_SIZE, init_work); + return arch_early_irq_init(); +} struct irq_desc *irq_to_desc(unsigned int irq) { - struct irq_desc *desc, *desc_pri; - int i; - int count = 0; - unsigned long phys; - unsigned long total_bytes; + if (irq_desc_ptrs && irq < nr_irqs) + return irq_desc_ptrs[irq]; - BUG_ON(irq == -1U); + return NULL; +} - desc_pri = desc = &sparse_irqs[0]; - while (desc) { - if (desc->irq == irq) - return desc; +struct irq_desc *irq_to_desc_alloc_cpu(unsigned int irq, int cpu) +{ + struct irq_desc *desc; + unsigned long flags; + int node; - if (desc->irq == -1U) { - desc->irq = irq; - return desc; - } - desc_pri = desc; - desc = desc->next; - count++; + if (irq >= nr_irqs) { + WARN(1, "irq (%d) >= nr_irqs (%d) in irq_to_desc_alloc\n", + irq, nr_irqs); + return NULL; } - /* - * we run out of pre-allocate ones, allocate more - */ - printk(KERN_DEBUG "try to get more irq_desc %d\n", nr_irq_desc); - - total_bytes = sizeof(struct irq_desc) * nr_irq_desc; - if (after_bootmem) - desc = kzalloc(total_bytes, GFP_ATOMIC); - else - desc = __alloc_bootmem_nopanic(total_bytes, PAGE_SIZE, 0); - - if (!desc) - panic("please boot with nr_irq_desc= %d\n", count * 2); + desc = irq_desc_ptrs[irq]; + if (desc) + return desc; - phys = __pa(desc); - printk(KERN_DEBUG "irq_desc ==> [%#lx - %#lx]\n", phys, phys + total_bytes); + spin_lock_irqsave(&sparse_irq_lock, flags); - for (i = 0; i < nr_irq_desc; i++) - init_one_irq_desc(&desc[i]); + /* We have to check it to avoid races with another CPU */ + desc = irq_desc_ptrs[irq]; + if (desc) + goto out_unlock; - for (i = 1; i < nr_irq_desc; i++) - desc[i-1].next = &desc[i]; + node = cpu_to_node(cpu); + desc = kzalloc_node(sizeof(*desc), GFP_ATOMIC, node); + printk(KERN_DEBUG " alloc irq_desc for %d on cpu %d node %d\n", + irq, cpu, node); + if (!desc) { + printk(KERN_ERR "can not alloc irq_desc\n"); + BUG_ON(1); + } + init_one_irq_desc(irq, desc, cpu); - /* init kstat_irqs, nr_cpu_ids is ready already */ - init_kstat_irqs(desc, nr_irq_desc, nr_cpu_ids); + irq_desc_ptrs[irq] = desc; - desc->irq = irq; - desc_pri->next = desc; +out_unlock: + spin_unlock_irqrestore(&sparse_irq_lock, flags); return desc; } -#else -static struct irq_desc *irq_desc; -DEFINE_DYN_ARRAY(irq_desc, sizeof(struct irq_desc), nr_irqs, PAGE_SIZE, init_work); - -#endif - -#else +#else /* !CONFIG_SPARSE_IRQ */ struct irq_desc irq_desc[NR_IRQS] __cacheline_aligned_in_smp = { [0 ... NR_IRQS-1] = { @@ -222,24 +236,47 @@ struct irq_desc irq_desc[NR_IRQS] __cacheline_aligned_in_smp = { .chip = &no_irq_chip, .handle_irq = handle_bad_irq, .depth = 1, - .lock = __SPIN_LOCK_UNLOCKED(sparse_irqs->lock), -#ifdef CONFIG_SMP - .affinity = CPU_MASK_ALL -#endif + .lock = __SPIN_LOCK_UNLOCKED(irq_desc->lock), } }; -#endif +static unsigned int kstat_irqs_all[NR_IRQS][NR_CPUS]; +int __init early_irq_init(void) +{ + struct irq_desc *desc; + int count; + int i; + + init_irq_default_affinity(); + + printk(KERN_INFO "NR_IRQS:%d\n", NR_IRQS); + + desc = irq_desc; + count = ARRAY_SIZE(irq_desc); + + for (i = 0; i < count; i++) { + desc[i].irq = i; + init_alloc_desc_masks(&desc[i], 0, true); + desc[i].kstat_irqs = kstat_irqs_all[i]; + } + return arch_early_irq_init(); +} -#ifndef CONFIG_HAVE_SPARSE_IRQ struct irq_desc *irq_to_desc(unsigned int irq) { - if (irq < nr_irqs) - return &irq_desc[irq]; + return (irq < NR_IRQS) ? irq_desc + irq : NULL; +} - return NULL; +struct irq_desc *irq_to_desc_alloc_cpu(unsigned int irq, int cpu) +{ + return irq_to_desc(irq); +} +#endif /* !CONFIG_SPARSE_IRQ */ + +void clear_kstat_irqs(struct irq_desc *desc) +{ + memset(desc->kstat_irqs, 0, nr_cpu_ids * sizeof(*(desc->kstat_irqs))); } -#endif /* * What should we do if we get a hw irq event on an illegal vector? @@ -247,9 +284,8 @@ struct irq_desc *irq_to_desc(unsigned int irq) */ static void ack_bad(unsigned int irq) { - struct irq_desc *desc; + struct irq_desc *desc = irq_to_desc(irq); - desc = irq_to_desc(irq); print_irq_desc(irq, desc); ack_bad_irq(irq); } @@ -303,6 +339,15 @@ irqreturn_t no_action(int cpl, void *dev_id) return IRQ_NONE; } +static void warn_no_thread(unsigned int irq, struct irqaction *action) +{ + if (test_and_set_bit(IRQTF_WARNED, &action->thread_flags)) + return; + + printk(KERN_WARNING "IRQ %d device %s returned IRQ_WAKE_THREAD " + "but no thread function available.", irq, action->name); +} + /** * handle_IRQ_event - irq action chain handler * @irq: the interrupt number @@ -319,9 +364,50 @@ irqreturn_t handle_IRQ_event(unsigned int irq, struct irqaction *action) local_irq_enable_in_hardirq(); do { + trace_irq_handler_entry(irq, action); ret = action->handler(irq, action->dev_id); - if (ret == IRQ_HANDLED) + trace_irq_handler_exit(irq, action, ret); + + switch (ret) { + case IRQ_WAKE_THREAD: + /* + * Set result to handled so the spurious check + * does not trigger. + */ + ret = IRQ_HANDLED; + + /* + * Catch drivers which return WAKE_THREAD but + * did not set up a thread function + */ + if (unlikely(!action->thread_fn)) { + warn_no_thread(irq, action); + break; + } + + /* + * Wake up the handler thread for this + * action. In case the thread crashed and was + * killed we just pretend that we handled the + * interrupt. The hardirq handler above has + * disabled the device interrupt, so no irq + * storm is lurking. + */ + if (likely(!test_bit(IRQTF_DIED, + &action->thread_flags))) { + set_bit(IRQTF_RUNTHREAD, &action->thread_flags); + wake_up_process(action->thread); + } + + /* Fall through to add to randomness */ + case IRQ_HANDLED: status |= action->flags; + break; + + default: + break; + } + retval |= ret; action = action->next; } while (action); @@ -334,6 +420,11 @@ irqreturn_t handle_IRQ_event(unsigned int irq, struct irqaction *action) } #ifndef CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ + +#ifdef CONFIG_ENABLE_WARN_DEPRECATED +# warning __do_IRQ is deprecated. Please convert to proper flow handlers +#endif + /** * __do_IRQ - original all in one highlevel IRQ handler * @irq: the interrupt number @@ -351,15 +442,19 @@ unsigned int __do_IRQ(unsigned int irq) struct irqaction *action; unsigned int status; - kstat_irqs_this_cpu(desc)++; + kstat_incr_irqs_this_cpu(irq, desc); + if (CHECK_IRQ_PER_CPU(desc->status)) { irqreturn_t action_ret; /* * No locking required for CPU-local interrupts: */ - if (desc->chip->ack) + if (desc->chip->ack) { desc->chip->ack(irq); + /* get new one */ + desc = irq_remap_to_desc(irq, desc); + } if (likely(!(desc->status & IRQ_DISABLED))) { action_ret = handle_IRQ_event(irq, desc->action); if (!noirqdebug) @@ -370,8 +465,10 @@ unsigned int __do_IRQ(unsigned int irq) } spin_lock(&desc->lock); - if (desc->chip->ack) + if (desc->chip->ack) { desc->chip->ack(irq); + desc = irq_remap_to_desc(irq, desc); + } /* * REPLAY is when Linux resends an IRQ that was dropped earlier * WAITING is used by probe to mark irqs that are being tested @@ -438,23 +535,20 @@ out: } #endif - -#ifdef CONFIG_TRACE_IRQFLAGS void early_init_irq_lock_class(void) { -#ifndef CONFIG_HAVE_DYN_ARRAY + struct irq_desc *desc; int i; - for (i = 0; i < nr_irqs; i++) - lockdep_set_class(&irq_desc[i].lock, &irq_desc_lock_class); -#endif + for_each_irq_desc(i, desc) { + lockdep_set_class(&desc->lock, &irq_desc_lock_class); + } } -#endif unsigned int kstat_irqs_cpu(unsigned int irq, int cpu) { struct irq_desc *desc = irq_to_desc(irq); - return desc->kstat_irqs[cpu]; + return desc ? desc->kstat_irqs[cpu] : 0; } EXPORT_SYMBOL(kstat_irqs_cpu);