summaryrefslogtreecommitdiffstats
path: root/arch/alpha/kernel/irq_smp.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/alpha/kernel/irq_smp.c')
-rw-r--r--arch/alpha/kernel/irq_smp.c250
1 files changed, 250 insertions, 0 deletions
diff --git a/arch/alpha/kernel/irq_smp.c b/arch/alpha/kernel/irq_smp.c
new file mode 100644
index 000000000..90a3c30b2
--- /dev/null
+++ b/arch/alpha/kernel/irq_smp.c
@@ -0,0 +1,250 @@
+/*
+ * linux/arch/alpha/kernel/irq_smp.c
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/random.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/irq.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+
+
+/* Who has global_irq_lock. */
+int global_irq_holder = NO_PROC_ID;
+
+/* This protects IRQ's. */
+spinlock_t global_irq_lock = SPIN_LOCK_UNLOCKED;
+
+/* Global IRQ locking depth. */
+static void *previous_irqholder = NULL;
+
+#define MAXCOUNT 100000000
+
+
+static void
+show(char * str, void *where)
+{
+#if 0
+ int i;
+ unsigned long *stack;
+#endif
+ int cpu = smp_processor_id();
+
+ printk("\n%s, CPU %d: %p\n", str, cpu, where);
+ printk("irq: %d [%d %d]\n",
+ irqs_running(),
+ cpu_data[0].irq_count,
+ cpu_data[1].irq_count);
+
+ printk("bh: %d [%d %d]\n",
+ spin_is_locked(&global_bh_lock) ? 1 : 0,
+ cpu_data[0].bh_count,
+ cpu_data[1].bh_count);
+#if 0
+ stack = (unsigned long *) &str;
+ for (i = 40; i ; i--) {
+ unsigned long x = *++stack;
+ if (x > (unsigned long) &init_task_union &&
+ x < (unsigned long) &vsprintf) {
+ printk("<[%08lx]> ", x);
+ }
+ }
+#endif
+}
+
+static inline void
+wait_on_irq(int cpu, void *where)
+{
+ 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 */
+ spin_unlock(&global_irq_lock);
+
+ for (;;) {
+ if (!--count) {
+ show("wait_on_irq", where);
+ count = MAXCOUNT;
+ }
+ __sti();
+ udelay(1); /* make sure to run pending irqs */
+ __cli();
+
+ if (irqs_running())
+ continue;
+ if (spin_is_locked(&global_irq_lock))
+ continue;
+ if (!local_bh_count(cpu)
+ && spin_is_locked(&global_bh_lock))
+ continue;
+ if (spin_trylock(&global_irq_lock))
+ break;
+ }
+ }
+}
+
+static inline void
+get_irqlock(int cpu, void* where)
+{
+ if (!spin_trylock(&global_irq_lock)) {
+ /* Do we already hold the lock? */
+ if (cpu == global_irq_holder)
+ return;
+ /* Uhhuh.. Somebody else got it. Wait. */
+ spin_lock(&global_irq_lock);
+ }
+
+ /*
+ * Ok, we got the lock bit.
+ * But that's actually just the easy part.. Now
+ * we need to make sure that nobody else is running
+ * in an interrupt context.
+ */
+ wait_on_irq(cpu, where);
+
+ /*
+ * Finally.
+ */
+#if DEBUG_SPINLOCK
+ global_irq_lock.task = current;
+ global_irq_lock.previous = where;
+#endif
+ global_irq_holder = cpu;
+ previous_irqholder = where;
+}
+
+void
+__global_cli(void)
+{
+ int cpu = smp_processor_id();
+ void *where = __builtin_return_address(0);
+
+ /*
+ * Maximize ipl. If ipl was previously 0 and if this thread
+ * is not in an irq, then take global_irq_lock.
+ */
+ if (swpipl(IPL_MAX) == IPL_MIN && !local_irq_count(cpu))
+ get_irqlock(cpu, where);
+}
+
+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 & 7));
+ /* 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(KERN_ERR "global_restore_flags: %08lx (%p)\n",
+ flags, __builtin_return_address(0));
+ }
+}
+
+/*
+ * From its use, I infer that synchronize_irq() stalls a thread until
+ * the effects of a command to an external device are known to have
+ * taken hold. Typically, the command is to stop sending interrupts.
+ * The strategy here is wait until there is at most one processor
+ * (this one) in an irq. The memory barrier serializes the write to
+ * the device and the subsequent accesses of global_irq_count.
+ * --jmartin
+ */
+#define DEBUG_SYNCHRONIZE_IRQ 0
+
+void
+synchronize_irq(void)
+{
+#if 0
+ /* Joe's version. */
+ int cpu = smp_processor_id();
+ int local_count;
+ int global_count;
+ int countdown = 1<<24;
+ void *where = __builtin_return_address(0);
+
+ mb();
+ do {
+ local_count = local_irq_count(cpu);
+ global_count = atomic_read(&global_irq_count);
+ if (DEBUG_SYNCHRONIZE_IRQ && (--countdown == 0)) {
+ printk("%d:%d/%d\n", cpu, local_count, global_count);
+ show("synchronize_irq", where);
+ break;
+ }
+ } while (global_count != local_count);
+#else
+ /* Jay's version. */
+ if (irqs_running()) {
+ cli();
+ sti();
+ }
+#endif
+}