aboutsummaryrefslogtreecommitdiffstats
path: root/arch/powerpc/kernel/irq.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/powerpc/kernel/irq.c')
-rw-r--r--arch/powerpc/kernel/irq.c84
1 files changed, 66 insertions, 18 deletions
diff --git a/arch/powerpc/kernel/irq.c b/arch/powerpc/kernel/irq.c
index fd4ddb858dbd..b4432332341f 100644
--- a/arch/powerpc/kernel/irq.c
+++ b/arch/powerpc/kernel/irq.c
@@ -323,7 +323,8 @@ EXPORT_SYMBOL(do_softirq);
323 323
324static LIST_HEAD(irq_hosts); 324static LIST_HEAD(irq_hosts);
325static spinlock_t irq_big_lock = SPIN_LOCK_UNLOCKED; 325static spinlock_t irq_big_lock = SPIN_LOCK_UNLOCKED;
326 326static DEFINE_PER_CPU(unsigned int, irq_radix_reader);
327static unsigned int irq_radix_writer;
327struct irq_map_entry irq_map[NR_IRQS]; 328struct irq_map_entry irq_map[NR_IRQS];
328static unsigned int irq_virq_count = NR_IRQS; 329static unsigned int irq_virq_count = NR_IRQS;
329static struct irq_host *irq_default_host; 330static struct irq_host *irq_default_host;
@@ -456,6 +457,58 @@ void irq_set_virq_count(unsigned int count)
456 irq_virq_count = count; 457 irq_virq_count = count;
457} 458}
458 459
460/* radix tree not lockless safe ! we use a brlock-type mecanism
461 * for now, until we can use a lockless radix tree
462 */
463static void irq_radix_wrlock(unsigned long *flags)
464{
465 unsigned int cpu, ok;
466
467 spin_lock_irqsave(&irq_big_lock, *flags);
468 irq_radix_writer = 1;
469 smp_mb();
470 do {
471 barrier();
472 ok = 1;
473 for_each_possible_cpu(cpu) {
474 if (per_cpu(irq_radix_reader, cpu)) {
475 ok = 0;
476 break;
477 }
478 }
479 if (!ok)
480 cpu_relax();
481 } while(!ok);
482}
483
484static void irq_radix_wrunlock(unsigned long flags)
485{
486 smp_wmb();
487 irq_radix_writer = 0;
488 spin_unlock_irqrestore(&irq_big_lock, flags);
489}
490
491static void irq_radix_rdlock(unsigned long *flags)
492{
493 local_irq_save(*flags);
494 __get_cpu_var(irq_radix_reader) = 1;
495 smp_mb();
496 if (likely(irq_radix_writer == 0))
497 return;
498 __get_cpu_var(irq_radix_reader) = 0;
499 smp_wmb();
500 spin_lock(&irq_big_lock);
501 __get_cpu_var(irq_radix_reader) = 1;
502 spin_unlock(&irq_big_lock);
503}
504
505static void irq_radix_rdunlock(unsigned long flags)
506{
507 __get_cpu_var(irq_radix_reader) = 0;
508 local_irq_restore(flags);
509}
510
511
459unsigned int irq_create_mapping(struct irq_host *host, 512unsigned int irq_create_mapping(struct irq_host *host,
460 irq_hw_number_t hwirq) 513 irq_hw_number_t hwirq)
461{ 514{
@@ -605,13 +658,9 @@ void irq_dispose_mapping(unsigned int virq)
605 /* Check if radix tree allocated yet */ 658 /* Check if radix tree allocated yet */
606 if (host->revmap_data.tree.gfp_mask == 0) 659 if (host->revmap_data.tree.gfp_mask == 0)
607 break; 660 break;
608 /* XXX radix tree not safe ! remove lock whem it becomes safe 661 irq_radix_wrlock(&flags);
609 * and use some RCU sync to make sure everything is ok before we
610 * can re-use that map entry
611 */
612 spin_lock_irqsave(&irq_big_lock, flags);
613 radix_tree_delete(&host->revmap_data.tree, hwirq); 662 radix_tree_delete(&host->revmap_data.tree, hwirq);
614 spin_unlock_irqrestore(&irq_big_lock, flags); 663 irq_radix_wrunlock(flags);
615 break; 664 break;
616 } 665 }
617 666
@@ -678,25 +727,24 @@ unsigned int irq_radix_revmap(struct irq_host *host,
678 if (tree->gfp_mask == 0) 727 if (tree->gfp_mask == 0)
679 return irq_find_mapping(host, hwirq); 728 return irq_find_mapping(host, hwirq);
680 729
681 /* XXX Current radix trees are NOT SMP safe !!! Remove that lock
682 * when that is fixed (when Nick's patch gets in
683 */
684 spin_lock_irqsave(&irq_big_lock, flags);
685
686 /* Now try to resolve */ 730 /* Now try to resolve */
731 irq_radix_rdlock(&flags);
687 ptr = radix_tree_lookup(tree, hwirq); 732 ptr = radix_tree_lookup(tree, hwirq);
733 irq_radix_rdunlock(flags);
734
688 /* Found it, return */ 735 /* Found it, return */
689 if (ptr) { 736 if (ptr) {
690 virq = ptr - irq_map; 737 virq = ptr - irq_map;
691 goto bail; 738 return virq;
692 } 739 }
693 740
694 /* If not there, try to insert it */ 741 /* If not there, try to insert it */
695 virq = irq_find_mapping(host, hwirq); 742 virq = irq_find_mapping(host, hwirq);
696 if (virq != NO_IRQ) 743 if (virq != NO_IRQ) {
744 irq_radix_wrlock(&flags);
697 radix_tree_insert(tree, hwirq, &irq_map[virq]); 745 radix_tree_insert(tree, hwirq, &irq_map[virq]);
698 bail: 746 irq_radix_wrunlock(flags);
699 spin_unlock_irqrestore(&irq_big_lock, flags); 747 }
700 return virq; 748 return virq;
701} 749}
702 750
@@ -807,12 +855,12 @@ static int irq_late_init(void)
807 struct irq_host *h; 855 struct irq_host *h;
808 unsigned long flags; 856 unsigned long flags;
809 857
810 spin_lock_irqsave(&irq_big_lock, flags); 858 irq_radix_wrlock(&flags);
811 list_for_each_entry(h, &irq_hosts, link) { 859 list_for_each_entry(h, &irq_hosts, link) {
812 if (h->revmap_type == IRQ_HOST_MAP_TREE) 860 if (h->revmap_type == IRQ_HOST_MAP_TREE)
813 INIT_RADIX_TREE(&h->revmap_data.tree, GFP_ATOMIC); 861 INIT_RADIX_TREE(&h->revmap_data.tree, GFP_ATOMIC);
814 } 862 }
815 spin_unlock_irqrestore(&irq_big_lock, flags); 863 irq_radix_wrunlock(flags);
816 864
817 return 0; 865 return 0;
818} 866}