patch-2_6_7-vs1_9_1_12
[linux-2.6.git] / arch / ppc64 / kernel / irq.c
index 5a4d3e4..7335442 100644 (file)
@@ -370,16 +370,13 @@ skip:
        return 0;
 }
 
-static inline int handle_irq_event(int irq, struct pt_regs *regs,
-                                  struct irqaction *action)
+int handle_irq_event(int irq, struct pt_regs *regs, struct irqaction *action)
 {
        int status = 0;
        int retval = 0;
 
-#ifndef CONFIG_PPC_ISERIES
        if (!(action->flags & SA_INTERRUPT))
                local_irq_enable();
-#endif
 
        do {
                status |= action->flags;
@@ -388,9 +385,7 @@ static inline int handle_irq_event(int irq, struct pt_regs *regs,
        } while (action);
        if (status & SA_SAMPLE_RANDOM)
                add_interrupt_randomness(irq);
-#ifndef CONFIG_PPC_ISERIES
        local_irq_disable();
-#endif
        return retval;
 }
 
@@ -486,6 +481,9 @@ void ppc_irq_dispatch_handler(struct pt_regs *regs, int irq)
        int cpu = smp_processor_id();
        irq_desc_t *desc = get_irq_desc(irq);
        irqreturn_t action_ret;
+#ifdef CONFIG_IRQSTACKS
+       struct thread_info *curtp, *irqtp;
+#endif
 
        kstat_cpu(cpu).irqs[irq]++;
 
@@ -552,7 +550,22 @@ void ppc_irq_dispatch_handler(struct pt_regs *regs, int irq)
         */
        for (;;) {
                spin_unlock(&desc->lock);
-               action_ret = handle_irq_event(irq, regs, action);
+
+#ifdef CONFIG_IRQSTACKS
+               /* Switch to the irq stack to handle this */
+               curtp = current_thread_info();
+               irqtp = hardirq_ctx[smp_processor_id()];
+               if (curtp != irqtp) {
+                       irqtp->task = curtp->task;
+                       irqtp->flags = 0;
+                       action_ret = call_handle_irq_event(irq, regs, action, irqtp);
+                       irqtp->task = NULL;
+                       if (irqtp->flags)
+                               set_bits(irqtp->flags, &curtp->flags);
+               } else
+#endif
+                       action_ret = handle_irq_event(irq, regs, action);
+
                spin_lock(&desc->lock);
                if (!noirqdebug)
                        note_interrupt(irq, desc, action_ret);
@@ -628,6 +641,21 @@ int do_IRQ(struct pt_regs *regs)
 
        irq_enter();
 
+#ifdef CONFIG_DEBUG_STACKOVERFLOW
+       /* Debugging check for stack overflow: is there less than 4KB free? */
+       {
+               long sp;
+
+               sp = __get_SP() & (THREAD_SIZE-1);
+
+               if (unlikely(sp < (sizeof(struct thread_info) + 4096))) {
+                       printk("do_IRQ: stack overflow: %ld\n",
+                               sp - sizeof(struct thread_info));
+                       dump_stack();
+               }
+       }
+#endif
+
        /*
         * Every arch is required to implement ppc_md.get_irq.
         * This function will either return an irq number or -1 to
@@ -679,6 +707,7 @@ void __init init_IRQ(void)
        once++;
 
        ppc_md.init_IRQ();
+       irq_ctx_init();
 }
 
 static struct proc_dir_entry * root_irq_dir;
@@ -702,7 +731,7 @@ static int irq_affinity_read_proc (char *page, char **start, off_t off,
        return len;
 }
 
-static int irq_affinity_write_proc (struct file *file, const char *buffer,
+static int irq_affinity_write_proc (struct file *file, const char __user *buffer,
                                        unsigned long count, void *data)
 {
        unsigned int irq = (long)data;
@@ -962,4 +991,51 @@ unsigned int real_irq_to_virt_slowpath(unsigned int real_irq)
 
 }
 
-#endif
+#endif /* CONFIG_PPC_ISERIES */
+
+#ifdef CONFIG_IRQSTACKS
+struct thread_info *softirq_ctx[NR_CPUS];
+struct thread_info *hardirq_ctx[NR_CPUS];
+
+void irq_ctx_init(void)
+{
+       struct thread_info *tp;
+       int i;
+
+       for (i = 0; i < NR_CPUS; i++) {
+               memset((void *)softirq_ctx[i], 0, THREAD_SIZE);
+               tp = softirq_ctx[i];
+               tp->cpu = i;
+               tp->preempt_count = SOFTIRQ_OFFSET;
+
+               memset((void *)hardirq_ctx[i], 0, THREAD_SIZE);
+               tp = hardirq_ctx[i];
+               tp->cpu = i;
+               tp->preempt_count = HARDIRQ_OFFSET;
+       }
+}
+
+void do_softirq(void)
+{
+       unsigned long flags;
+       struct thread_info *curtp, *irqtp;
+
+       if (in_interrupt())
+               return;
+
+       local_irq_save(flags);
+
+       if (local_softirq_pending()) {
+               curtp = current_thread_info();
+               irqtp = softirq_ctx[smp_processor_id()];
+               irqtp->task = curtp->task;
+               call_do_softirq(irqtp);
+               irqtp->task = NULL;
+       }
+
+       local_irq_restore(flags);
+}
+EXPORT_SYMBOL(do_softirq);
+
+#endif /* CONFIG_IRQSTACKS */
+