]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - arch/powerpc/kernel/irq.c
Merge branch 'davem-fixes' of master.kernel.org:/pub/scm/linux/kernel/git/jgarzik...
[linux-2.6-omap-h63xx.git] / arch / powerpc / kernel / irq.c
index d972decf0324d4b92e748f4a271e4ad3b4ed4475..ac222d0ab12e86406bcbc6247adb3b8150ed49ee 100644 (file)
@@ -439,8 +439,8 @@ void do_softirq(void)
 
 static LIST_HEAD(irq_hosts);
 static DEFINE_SPINLOCK(irq_big_lock);
-static DEFINE_PER_CPU(unsigned int, irq_radix_reader);
-static unsigned int irq_radix_writer;
+static unsigned int revmap_trees_allocated;
+static DEFINE_MUTEX(revmap_trees_mutex);
 struct irq_map_entry irq_map[NR_IRQS];
 static unsigned int irq_virq_count = NR_IRQS;
 static struct irq_host *irq_default_host;
@@ -583,57 +583,6 @@ void irq_set_virq_count(unsigned int count)
                irq_virq_count = count;
 }
 
-/* radix tree not lockless safe ! we use a brlock-type mecanism
- * for now, until we can use a lockless radix tree
- */
-static void irq_radix_wrlock(unsigned long *flags)
-{
-       unsigned int cpu, ok;
-
-       spin_lock_irqsave(&irq_big_lock, *flags);
-       irq_radix_writer = 1;
-       smp_mb();
-       do {
-               barrier();
-               ok = 1;
-               for_each_possible_cpu(cpu) {
-                       if (per_cpu(irq_radix_reader, cpu)) {
-                               ok = 0;
-                               break;
-                       }
-               }
-               if (!ok)
-                       cpu_relax();
-       } while(!ok);
-}
-
-static void irq_radix_wrunlock(unsigned long flags)
-{
-       smp_wmb();
-       irq_radix_writer = 0;
-       spin_unlock_irqrestore(&irq_big_lock, flags);
-}
-
-static void irq_radix_rdlock(unsigned long *flags)
-{
-       local_irq_save(*flags);
-       __get_cpu_var(irq_radix_reader) = 1;
-       smp_mb();
-       if (likely(irq_radix_writer == 0))
-               return;
-       __get_cpu_var(irq_radix_reader) = 0;
-       smp_wmb();
-       spin_lock(&irq_big_lock);
-       __get_cpu_var(irq_radix_reader) = 1;
-       spin_unlock(&irq_big_lock);
-}
-
-static void irq_radix_rdunlock(unsigned long flags)
-{
-       __get_cpu_var(irq_radix_reader) = 0;
-       local_irq_restore(flags);
-}
-
 static int irq_setup_virq(struct irq_host *host, unsigned int virq,
                            irq_hw_number_t hwirq)
 {
@@ -788,7 +737,6 @@ void irq_dispose_mapping(unsigned int virq)
 {
        struct irq_host *host;
        irq_hw_number_t hwirq;
-       unsigned long flags;
 
        if (virq == NO_IRQ)
                return;
@@ -821,12 +769,16 @@ void irq_dispose_mapping(unsigned int virq)
                        host->revmap_data.linear.revmap[hwirq] = NO_IRQ;
                break;
        case IRQ_HOST_MAP_TREE:
-               /* Check if radix tree allocated yet */
-               if (host->revmap_data.tree.gfp_mask == 0)
+               /*
+                * Check if radix tree allocated yet, if not then nothing to
+                * remove.
+                */
+               smp_rmb();
+               if (revmap_trees_allocated < 1)
                        break;
-               irq_radix_wrlock(&flags);
+               mutex_lock(&revmap_trees_mutex);
                radix_tree_delete(&host->revmap_data.tree, hwirq);
-               irq_radix_wrunlock(flags);
+               mutex_unlock(&revmap_trees_mutex);
                break;
        }
 
@@ -875,43 +827,62 @@ unsigned int irq_find_mapping(struct irq_host *host,
 EXPORT_SYMBOL_GPL(irq_find_mapping);
 
 
-unsigned int irq_radix_revmap(struct irq_host *host,
-                             irq_hw_number_t hwirq)
+unsigned int irq_radix_revmap_lookup(struct irq_host *host,
+                                    irq_hw_number_t hwirq)
 {
-       struct radix_tree_root *tree;
        struct irq_map_entry *ptr;
        unsigned int virq;
-       unsigned long flags;
 
        WARN_ON(host->revmap_type != IRQ_HOST_MAP_TREE);
 
-       /* Check if the radix tree exist yet. We test the value of
-        * the gfp_mask for that. Sneaky but saves another int in the
-        * structure. If not, we fallback to slow mode
+       /*
+        * Check if the radix tree exists and has bee initialized.
+        * If not, we fallback to slow mode
         */
-       tree = &host->revmap_data.tree;
-       if (tree->gfp_mask == 0)
+       if (revmap_trees_allocated < 2)
                return irq_find_mapping(host, hwirq);
 
        /* Now try to resolve */
-       irq_radix_rdlock(&flags);
-       ptr = radix_tree_lookup(tree, hwirq);
-       irq_radix_rdunlock(flags);
+       /*
+        * No rcu_read_lock(ing) needed, the ptr returned can't go under us
+        * as it's referencing an entry in the static irq_map table.
+        */
+       ptr = radix_tree_lookup(&host->revmap_data.tree, hwirq);
 
-       /* Found it, return */
-       if (ptr) {
+       /*
+        * If found in radix tree, then fine.
+        * Else fallback to linear lookup - this should not happen in practice
+        * as it means that we failed to insert the node in the radix tree.
+        */
+       if (ptr)
                virq = ptr - irq_map;
-               return virq;
-       }
+       else
+               virq = irq_find_mapping(host, hwirq);
+
+       return virq;
+}
+
+void irq_radix_revmap_insert(struct irq_host *host, unsigned int virq,
+                            irq_hw_number_t hwirq)
+{
+
+       WARN_ON(host->revmap_type != IRQ_HOST_MAP_TREE);
+
+       /*
+        * Check if the radix tree exists yet.
+        * If not, then the irq will be inserted into the tree when it gets
+        * initialized.
+        */
+       smp_rmb();
+       if (revmap_trees_allocated < 1)
+               return;
 
-       /* If not there, try to insert it */
-       virq = irq_find_mapping(host, hwirq);
        if (virq != NO_IRQ) {
-               irq_radix_wrlock(&flags);
-               radix_tree_insert(tree, hwirq, &irq_map[virq]);
-               irq_radix_wrunlock(flags);
+               mutex_lock(&revmap_trees_mutex);
+               radix_tree_insert(&host->revmap_data.tree, hwirq,
+                                 &irq_map[virq]);
+               mutex_unlock(&revmap_trees_mutex);
        }
-       return virq;
 }
 
 unsigned int irq_linear_revmap(struct irq_host *host,
@@ -1020,14 +991,44 @@ void irq_early_init(void)
 static int irq_late_init(void)
 {
        struct irq_host *h;
-       unsigned long flags;
+       unsigned int i;
 
-       irq_radix_wrlock(&flags);
+       /*
+        * No mutual exclusion with respect to accessors of the tree is needed
+        * here as the synchronization is done via the state variable
+        * revmap_trees_allocated.
+        */
        list_for_each_entry(h, &irq_hosts, link) {
                if (h->revmap_type == IRQ_HOST_MAP_TREE)
-                       INIT_RADIX_TREE(&h->revmap_data.tree, GFP_ATOMIC);
+                       INIT_RADIX_TREE(&h->revmap_data.tree, GFP_KERNEL);
+       }
+
+       /*
+        * Make sure the radix trees inits are visible before setting
+        * the flag
+        */
+       smp_wmb();
+       revmap_trees_allocated = 1;
+
+       /*
+        * Insert the reverse mapping for those interrupts already present
+        * in irq_map[].
+        */
+       mutex_lock(&revmap_trees_mutex);
+       for (i = 0; i < irq_virq_count; i++) {
+               if (irq_map[i].host &&
+                   (irq_map[i].host->revmap_type == IRQ_HOST_MAP_TREE))
+                       radix_tree_insert(&irq_map[i].host->revmap_data.tree,
+                                         irq_map[i].hwirq, &irq_map[i]);
        }
-       irq_radix_wrunlock(flags);
+       mutex_unlock(&revmap_trees_mutex);
+
+       /*
+        * Make sure the radix trees insertions are visible before setting
+        * the flag
+        */
+       smp_wmb();
+       revmap_trees_allocated = 2;
 
        return 0;
 }