patch-2.3.49 linux/arch/alpha/kernel/irq_smp.c

Next file: linux/arch/alpha/kernel/irq_srm.c
Previous file: linux/arch/alpha/kernel/irq_impl.h
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.3.48/linux/arch/alpha/kernel/irq_smp.c linux/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
+}

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)