diff -Nru linux/arch/mips/kernel/irq.c.orig linux/arch/mips/kernel/irq.c --- linux/arch/mips/kernel/irq.c.orig Fri May 18 04:44:21 2001 +++ linux/arch/mips/kernel/irq.c Fri May 18 05:00:12 2001 @@ -8,17 +8,60 @@ * Copyright (C) 1992 Linus Torvalds * Copyright (C) 1994 - 2000 Ralf Baechle */ -#include -#include -#include + +/* + * (mostly architecture independent, will move to kernel/irq.c in 2.5.) + * + * IRQs are in fact implemented a bit like signal handlers for the kernel. + * Naturally it's not a 1:1 relation, but there are similarities. + */ + +/* + * Some minor modification for MIPS. + */ + +#include +#include +#include +#include +#include +#include #include -#include +#include #include -#include #include -#include +#include +#include +#include +#include +#include +#include +#include #include +#include +#include +#include +#include +#include + + + +/* + * Linux has a controller-independent x86 interrupt architecture. + * every controller has a 'controller-template', that is used + * by the main code to do the right thing. Each driver-visible + * interrupt source is transparently wired to the apropriate + * controller. Thus drivers need not be aware of the + * interrupt-controller. + * + * Various interrupt controllers we handle: 8259 PIC, SMP IO-APIC, + * PIIX4's internal 8259 PIC and SGI's Visual Workstation Cobalt (IO-)APIC. + * (IO-APICs assumed to be messaging to Pentium local-APICs) + * + * the code is designed to be easily extended with new/different + * interrupt controllers, without having to do assembly magic. + */ /* * Controller mappings for all interrupt sources: @@ -26,6 +69,8 @@ irq_desc_t irq_desc[NR_IRQS] __cacheline_aligned = { [0 ... NR_IRQS-1] = { 0, &no_irq_type, NULL, 0, SPIN_LOCK_UNLOCKED}}; +static void register_irq_proc (unsigned int irq); + /* * Special irq handlers. */ @@ -41,12 +86,25 @@ static void disable_none(unsigned int irq) { } static void ack_none(unsigned int irq) { +/* + * 'what should we do if we get a hw irq event on an illegal vector'. + * each architecture has to answer this themselves, it doesnt deserve + * a generic callback i think. + */ +#if CONFIG_X86 + printk("unexpected IRQ trap at vector %02x\n", irq); +#ifdef CONFIG_X86_LOCAL_APIC /* - * 'what should we do if we get a hw irq event on an illegal vector'. - * each architecture has to answer this themselves, it doesnt deserve - * a generic callback i think. + * Currently unexpected vectors happen only on SMP and APIC. + * We _must_ ack these because every local APIC has only N + * irq slots per priority level, and a 'hanging, unacked' IRQ + * holds up an irq slot - in excessive cases (when multiple + * unexpected vectors occur) that might lock up the APIC + * completely. */ - printk("unexpected interrupt %d\n", irq); + ack_APIC_irq(); +#endif +#endif } /* startup is the same as "enable", shutdown is same as "disable" */ @@ -63,7 +121,7 @@ end_none }; -volatile unsigned long irq_err_count, spurious_count; +volatile unsigned long spurious_count; /* * Generic, controller-independent functions: @@ -71,13 +129,13 @@ int get_irq_list(char *buf) { + int i, j; struct irqaction * action; char *p = buf; - int i; p += sprintf(p, " "); - for (i=0; i < 1 /*smp_num_cpus*/; i++) - p += sprintf(p, "CPU%d ", i); + for (j=0; jtypename); p += sprintf(p, " %s", action->name); @@ -93,10 +157,272 @@ p += sprintf(p, ", %s", action->name); *p++ = '\n'; } - p += sprintf(p, "ERR: %10lu\n", irq_err_count); +#if defined(STUPID_I386_STUFF) + p += sprintf(p, "NMI: "); + for (j = 0; j < smp_num_cpus; j++) + p += sprintf(p, "%10u ", + nmi_count(cpu_logical_map(j))); + p += sprintf(p, "\n"); +#endif +#if CONFIG_SMP + p += sprintf(p, "LOC: "); + for (j = 0; j < smp_num_cpus; j++) + p += sprintf(p, "%10u ", + apic_timer_irqs[cpu_logical_map(j)]); + p += sprintf(p, "\n"); +#endif + p += sprintf(p, "ERR: %10lu\n", spurious_count); return p - buf; } + +/* + * Global interrupt locks for SMP. Allow interrupts to come in on any + * CPU, yet make cli/sti act globally to protect critical regions.. + */ + +#ifdef CONFIG_SMP + +#error "SMP stuff does not look right for MIPS. For example, it refers to x86 register esp" + +unsigned char global_irq_holder = NO_PROC_ID; +unsigned volatile long global_irq_lock; /* pendantic: long for set_bit --RR */ + +extern void show_stack(unsigned long* esp); + +static void show(char * str) +{ + int i; + int cpu = smp_processor_id(); + + printk("\n%s, CPU %d:\n", str, cpu); + printk("irq: %d [",irqs_running()); + for(i=0;i < smp_num_cpus;i++) + printk(" %d",local_irq_count(i)); + printk(" ]\nbh: %d [",spin_is_locked(&global_bh_lock) ? 1 : 0); + for(i=0;i < smp_num_cpus;i++) + printk(" %d",local_bh_count(i)); + + printk(" ]\nStack dumps:"); + for(i = 0; i < smp_num_cpus; i++) { + unsigned long esp; + if (i == cpu) + continue; + printk("\nCPU %d:",i); + esp = init_tss[i].esp0; + if (!esp) { + /* tss->esp0 is set to NULL in cpu_init(), + * it's initialized when the cpu returns to user + * space. -- manfreds + */ + printk(" "); + continue; + } + esp &= ~(THREAD_SIZE-1); + esp += sizeof(struct task_struct); + show_stack((void*)esp); + } + printk("\nCPU %d:",cpu); + show_stack(NULL); + printk("\n"); +} + +#define MAXCOUNT 100000000 + +/* + * I had a lockup scenario where a tight loop doing + * spin_unlock()/spin_lock() on CPU#1 was racing with + * spin_lock() on CPU#0. CPU#0 should have noticed spin_unlock(), but + * apparently the spin_unlock() information did not make it + * through to CPU#0 ... nasty, is this by design, do we have to limit + * 'memory update oscillation frequency' artificially like here? + * + * Such 'high frequency update' races can be avoided by careful design, but + * some of our major constructs like spinlocks use similar techniques, + * it would be nice to clarify this issue. Set this define to 0 if you + * want to check whether your system freezes. I suspect the delay done + * by SYNC_OTHER_CORES() is in correlation with 'snooping latency', but + * i thought that such things are guaranteed by design, since we use + * the 'LOCK' prefix. + */ +#define SUSPECTED_CPU_OR_CHIPSET_BUG_WORKAROUND 0 + +#if SUSPECTED_CPU_OR_CHIPSET_BUG_WORKAROUND +# define SYNC_OTHER_CORES(x) udelay(x+1) +#else +/* + * We have to allow irqs to arrive between __sti and __cli + */ +# define SYNC_OTHER_CORES(x) __asm__ __volatile__ ("nop") +#endif + +static inline void wait_on_irq(int cpu) +{ + int count = MAXCOUNT; + + for (;;) { + + /* + * Wait until all interrupts are gone. Wait + * for bottom half handlers unless we're + * already executing in one.. + */ + if (!irqs_running()) + if (local_bh_count(cpu) || !spin_is_locked(&global_bh_lock)) + break; + + /* Duh, we have to loop. Release the lock to avoid deadlocks */ + clear_bit(0,&global_irq_lock); + + for (;;) { + if (!--count) { + show("wait_on_irq"); + count = ~0; + } + __sti(); + SYNC_OTHER_CORES(cpu); + __cli(); + if (irqs_running()) + continue; + if (global_irq_lock) + continue; + if (!local_bh_count(cpu) && spin_is_locked(&global_bh_lock)) + continue; + if (!test_and_set_bit(0,&global_irq_lock)) + break; + } + } +} + +/* + * This is called when we want to synchronize with + * interrupts. We may for example tell a device to + * stop sending interrupts: but to make sure there + * are no interrupts that are executing on another + * CPU we need to call this function. + */ +void synchronize_irq(void) +{ + if (irqs_running()) { + /* Stupid approach */ + cli(); + sti(); + } +} + +static inline void get_irqlock(int cpu) +{ + if (test_and_set_bit(0,&global_irq_lock)) { + /* do we already hold the lock? */ + if ((unsigned char) cpu == global_irq_holder) + return; + /* Uhhuh.. Somebody else got it. Wait.. */ + do { + do { + rep_nop(); + } while (test_bit(0,&global_irq_lock)); + } while (test_and_set_bit(0,&global_irq_lock)); + } + /* + * We also to make sure that nobody else is running + * in an interrupt context. + */ + wait_on_irq(cpu); + + /* + * Ok, finally.. + */ + global_irq_holder = cpu; +} + +#define EFLAGS_IF_SHIFT 9 + +/* + * A global "cli()" while in an interrupt context + * turns into just a local cli(). Interrupts + * should use spinlocks for the (very unlikely) + * case that they ever want to protect against + * each other. + * + * If we already have local interrupts disabled, + * this will not turn a local disable into a + * global one (problems with spinlocks: this makes + * save_flags+cli+sti usable inside a spinlock). + */ +void __global_cli(void) +{ + unsigned int flags; + + __save_flags(flags); + if (flags & (1 << EFLAGS_IF_SHIFT)) { + int cpu = smp_processor_id(); + __cli(); + if (!local_irq_count(cpu)) + get_irqlock(cpu); + } +} + +void __global_sti(void) +{ + int cpu = smp_processor_id(); + + if (!local_irq_count(cpu)) + release_irqlock(cpu); + __sti(); +} + +/* + * SMP flags value to restore to: + * 0 - global cli + * 1 - global sti + * 2 - local cli + * 3 - local sti + */ +unsigned long __global_save_flags(void) +{ + int retval; + int local_enabled; + unsigned long flags; + int cpu = smp_processor_id(); + + __save_flags(flags); + local_enabled = (flags >> EFLAGS_IF_SHIFT) & 1; + /* default to local */ + retval = 2 + local_enabled; + + /* check for global flags if we're not in an interrupt */ + if (!local_irq_count(cpu)) { + if (local_enabled) + retval = 1; + if (global_irq_holder == cpu) + retval = 0; + } + return retval; +} + +void __global_restore_flags(unsigned long flags) +{ + switch (flags) { + case 0: + __global_cli(); + break; + case 1: + __global_sti(); + break; + case 2: + __cli(); + break; + case 3: + __sti(); + break; + default: + printk("global_restore_flags: %08lx (%08lx)\n", + flags, (&flags)[-1]); + } +} + +#endif + /* * This should really return information about whether * we should do bottom half handling etc. Right now we @@ -228,7 +554,7 @@ * handlers). */ asmlinkage unsigned int do_IRQ(int irq, struct pt_regs *regs) -{ +{ /* * We ack quickly, we don't want the irq controller * thinking we're snobs just because some other CPU has @@ -690,18 +1016,162 @@ } spin_unlock_irqrestore(&desc->lock,flags); - /* register_irq_proc(irq); */ + register_irq_proc(irq); return 0; } -void __init init_generic_irq(void) +static struct proc_dir_entry * root_irq_dir; +static struct proc_dir_entry * irq_dir [NR_IRQS]; +static struct proc_dir_entry * smp_affinity_entry [NR_IRQS]; + +static unsigned long irq_affinity [NR_IRQS] = { [0 ... NR_IRQS-1] = ~0UL }; + +#define HEX_DIGITS 8 + +static int irq_affinity_read_proc (char *page, char **start, off_t off, + int count, int *eof, void *data) { + if (count < HEX_DIGITS+1) + return -EINVAL; + return sprintf (page, "%08lx\n", irq_affinity[(long)data]); +} + +static unsigned int parse_hex_value (const char *buffer, + unsigned long count, unsigned long *ret) +{ + unsigned char hexnum [HEX_DIGITS]; + unsigned long value; int i; - for (i = 0; i < NR_IRQS; i++) { - irq_desc[i].status = IRQ_DISABLED; - irq_desc[i].action = NULL; - irq_desc[i].depth = 1; - irq_desc[i].handler = &no_irq_type; + if (!count) + return -EINVAL; + if (count > HEX_DIGITS) + count = HEX_DIGITS; + if (copy_from_user(hexnum, buffer, count)) + return -EFAULT; + + /* + * Parse the first 8 characters as a hex string, any non-hex char + * is end-of-string. '00e1', 'e1', '00E1', 'E1' are all the same. + */ + value = 0; + + for (i = 0; i < count; i++) { + unsigned int c = hexnum[i]; + + switch (c) { + case '0' ... '9': c -= '0'; break; + case 'a' ... 'f': c -= 'a'-10; break; + case 'A' ... 'F': c -= 'A'-10; break; + default: + goto out; + } + value = (value << 4) | c; } +out: + *ret = value; + return 0; +} + +static int irq_affinity_write_proc (struct file *file, const char *buffer, + unsigned long count, void *data) +{ + int irq = (long) data, full_count = count, err; + unsigned long new_value; + + if (!irq_desc[irq].handler->set_affinity) + return -EIO; + + err = parse_hex_value(buffer, count, &new_value); + +#if CONFIG_SMP + /* + * Do not allow disabling IRQs completely - it's a too easy + * way to make the system unusable accidentally :-) At least + * one online CPU still has to be targeted. + */ + if (!(new_value & cpu_online_map)) + return -EINVAL; +#endif + + irq_affinity[irq] = new_value; + irq_desc[irq].handler->set_affinity(irq, new_value); + + return full_count; +} + +static int prof_cpu_mask_read_proc (char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + unsigned long *mask = (unsigned long *) data; + if (count < HEX_DIGITS+1) + return -EINVAL; + return sprintf (page, "%08lx\n", *mask); +} + +static int prof_cpu_mask_write_proc (struct file *file, const char *buffer, + unsigned long count, void *data) +{ + unsigned long *mask = (unsigned long *) data, full_count = count, err; + unsigned long new_value; + + err = parse_hex_value(buffer, count, &new_value); + if (err) + return err; + + *mask = new_value; + return full_count; +} + +#define MAX_NAMELEN 10 + +static void register_irq_proc (unsigned int irq) +{ + struct proc_dir_entry *entry; + char name [MAX_NAMELEN]; + + if (!root_irq_dir || (irq_desc[irq].handler == &no_irq_type) || + irq_dir[irq]) + return; + + memset(name, 0, MAX_NAMELEN); + sprintf(name, "%d", irq); + + /* create /proc/irq/1234 */ + irq_dir[irq] = proc_mkdir(name, root_irq_dir); + + /* create /proc/irq/1234/smp_affinity */ + entry = create_proc_entry("smp_affinity", 0600, irq_dir[irq]); + + entry->nlink = 1; + entry->data = (void *)(long)irq; + entry->read_proc = irq_affinity_read_proc; + entry->write_proc = irq_affinity_write_proc; + + smp_affinity_entry[irq] = entry; +} + +unsigned long prof_cpu_mask = -1; + +void init_irq_proc (void) +{ + struct proc_dir_entry *entry; + int i; + + /* create /proc/irq */ + root_irq_dir = proc_mkdir("irq", 0); + + /* create /proc/irq/prof_cpu_mask */ + entry = create_proc_entry("prof_cpu_mask", 0600, root_irq_dir); + + entry->nlink = 1; + entry->data = (void *)&prof_cpu_mask; + entry->read_proc = prof_cpu_mask_read_proc; + entry->write_proc = prof_cpu_mask_write_proc; + + /* + * Create entries for all existing IRQs. + */ + for (i = 0; i < NR_IRQS; i++) + register_irq_proc(i); } diff -Nru linux/arch/mips/kernel/irq_cpu.c.orig linux/arch/mips/kernel/irq_cpu.c --- linux/arch/mips/kernel/irq_cpu.c.orig Fri May 18 05:01:32 2001 +++ linux/arch/mips/kernel/irq_cpu.c Fri May 18 05:03:26 2001 @@ -0,0 +1,133 @@ +/*********************************************************************** + * Copyright 2001 MontaVista Software Inc. + * Author: Jun Sun, jsun@mvista.com or jsun@junsun.net + * + * arch/mips/kernel/irq_cpu.c + * This file define the irq handler for generic MIPS CPU interrupts. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + *********************************************************************** + */ + +/* + * Almost all MIPS CPUs define 8 interrupt sources. They are typically + * level triggered (i.e., cannot be cleared from CPU; must be cleared from + * device). The first two are software interrupts. The last one is + * usually cpu timer interrupt if coutner register is present. + * + * This file exports one global function: + * mips_cpu_irq_init(u32 irq_base); + */ + +#include +#include +#include +#include + +#include + +/* + * macro for catching spurious errors. Eable to LL_DEBUG in kernel hacking + * config menu. + */ +#ifdef CONFIG_LL_DEBUG + +#include + +#define MIPS_ASSERT(x) if (!(x)) { panic("MIPS_ASSERT failed at %s:%d\n", __FILE__, __LINE__); } +#define MIPS_VERIFY(x, y) MIPS_ASSERT(x y) +#define MIPS_DEBUG(x) do { x; } while (0) + +#else + +#define MIPS_ASSERT(x) +#define MIPS_VERIFY(x, y) x +#define MIPS_DEBUG(x) + +#endif + +static int mips_cpu_irq_base=-1; + +static void +mips_cpu_irq_enable(unsigned int irq) +{ + MIPS_ASSERT(mips_cpu_irq_base != -1); + MIPS_ASSERT(irq >= mips_cpu_irq_base); + MIPS_ASSERT(irq < mips_cpu_irq_base+8); + + clear_cp0_cause( 1 << (irq - mips_cpu_irq_base + 8)); + set_cp0_status(1 << (irq - mips_cpu_irq_base + 8)); +} + +static void +mips_cpu_irq_disable(unsigned int irq) +{ + MIPS_ASSERT(mips_cpu_irq_base != -1); + MIPS_ASSERT(irq >= mips_cpu_irq_base); + MIPS_ASSERT(irq < mips_cpu_irq_base+8); + + clear_cp0_status(1 << (irq - mips_cpu_irq_base + 8)); +} + +static unsigned int mips_cpu_irq_startup(unsigned int irq) +{ + mips_cpu_irq_enable(irq); + return 0; +} + +#define mips_cpu_irq_shutdown mips_cpu_irq_disable + +static void +mips_cpu_irq_ack(unsigned int irq) +{ + MIPS_ASSERT(mips_cpu_irq_base != -1); + MIPS_ASSERT(irq >= mips_cpu_irq_base); + MIPS_ASSERT(irq < mips_cpu_irq_base+8); + + /* although we attemp to clear the IP bit in cause reigster, I think + * usually it is cleared by device (irq source) + */ + clear_cp0_cause( 1 << (irq - mips_cpu_irq_base + 8)); + + /* I am not fully convinced that I should disable irq here */ +} + +static void +mips_cpu_irq_end(unsigned int irq) +{ + MIPS_ASSERT(mips_cpu_irq_base != -1); + MIPS_ASSERT(irq >= mips_cpu_irq_base); + MIPS_ASSERT(irq < mips_cpu_irq_base+8); + /* I am not fully convinced that I should enable irq here */ +} + +static hw_irq_controller mips_cpu_irq_controller = { + "CPU_irq", + mips_cpu_irq_startup, + mips_cpu_irq_shutdown, + mips_cpu_irq_enable, + mips_cpu_irq_disable, + mips_cpu_irq_ack, + mips_cpu_irq_end, + NULL /* no affinity stuff for UP */ +}; + + +void +mips_cpu_irq_init(u32 irq_base) +{ + extern irq_desc_t irq_desc[]; + u32 i; + + for (i= irq_base; i< irq_base+8; i++) { + irq_desc[i].status = IRQ_DISABLED; + irq_desc[i].action = NULL; + irq_desc[i].depth = 1; + irq_desc[i].handler = &mips_cpu_irq_controller; + } + + mips_cpu_irq_base = irq_base; +} diff -Nru linux/arch/mips/kernel/Makefile.orig linux/arch/mips/kernel/Makefile --- linux/arch/mips/kernel/Makefile.orig Fri May 18 04:44:21 2001 +++ linux/arch/mips/kernel/Makefile Fri May 18 05:04:36 2001 @@ -20,7 +20,7 @@ obj-y += branch.o process.o signal.o entry.o \ traps.o ptrace.o vm86.o ioport.o reset.o \ semaphore.o setup.o syscall.o sysmips.o \ - ipc.o scall_o32.o unaligned.o + ipc.o scall_o32.o unaligned.o irq_cpu.o obj-$(CONFIG_MODULES) += mips_ksyms.o ifdef CONFIG_CPU_R3000 diff -Nru linux/include/asm-mips/smp.h.orig linux/include/asm-mips/smp.h --- linux/include/asm-mips/smp.h.orig Thu Mar 29 10:22:59 2001 +++ linux/include/asm-mips/smp.h Fri May 18 04:56:21 2001 @@ -2,12 +2,12 @@ #define __ASM_MIPS_SMP_H #include -#include -#include -#include #ifdef CONFIG_SMP +#include +#include +#include /* Mappings are straight across. If we want to add support for disabling cpus and such,