aboutsummaryrefslogtreecommitdiffstats
path: root/arch/powerpc/kernel/irq.c
diff options
context:
space:
mode:
authorSebastien Dugue <sebastien.dugue@bull.net>2008-09-04 08:37:08 -0400
committerPaul Mackerras <paulus@samba.org>2008-09-15 14:08:45 -0400
commit150c6c8fecf6daaf68c2987ba2b6b259baefdff2 (patch)
treed624972d02c6e6da7966077b006b39a88ef5059b /arch/powerpc/kernel/irq.c
parent967e012ef306e99cfddcd7423f37414e6b568361 (diff)
powerpc: Make the irq reverse mapping radix tree lockless
The radix trees used by interrupt controllers for their irq reverse mapping (currently only the XICS found on pSeries) have a complex locking scheme dating back to before the advent of the lockless radix tree. This takes advantage of the lockless radix tree and of the fact that the items of the tree are pointers to a static array (irq_map) elements which can never go under us to simplify the locking. Concurrency between readers and writers is handled by the intrinsic properties of the lockless radix tree. Concurrency between writers is handled with a global mutex. Signed-off-by: Sebastien Dugue <sebastien.dugue@bull.net> Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org> Cc: Michael Ellerman <michael@ellerman.id.au> Signed-off-by: Paul Mackerras <paulus@samba.org>
Diffstat (limited to 'arch/powerpc/kernel/irq.c')
-rw-r--r--arch/powerpc/kernel/irq.c76
1 files changed, 11 insertions, 65 deletions
diff --git a/arch/powerpc/kernel/irq.c b/arch/powerpc/kernel/irq.c
index 2656924415da..ac222d0ab12e 100644
--- a/arch/powerpc/kernel/irq.c
+++ b/arch/powerpc/kernel/irq.c
@@ -439,9 +439,8 @@ void do_softirq(void)
439 439
440static LIST_HEAD(irq_hosts); 440static LIST_HEAD(irq_hosts);
441static DEFINE_SPINLOCK(irq_big_lock); 441static DEFINE_SPINLOCK(irq_big_lock);
442static DEFINE_PER_CPU(unsigned int, irq_radix_reader);
443static unsigned int irq_radix_writer;
444static unsigned int revmap_trees_allocated; 442static unsigned int revmap_trees_allocated;
443static DEFINE_MUTEX(revmap_trees_mutex);
445struct irq_map_entry irq_map[NR_IRQS]; 444struct irq_map_entry irq_map[NR_IRQS];
446static unsigned int irq_virq_count = NR_IRQS; 445static unsigned int irq_virq_count = NR_IRQS;
447static struct irq_host *irq_default_host; 446static struct irq_host *irq_default_host;
@@ -584,57 +583,6 @@ void irq_set_virq_count(unsigned int count)
584 irq_virq_count = count; 583 irq_virq_count = count;
585} 584}
586 585
587/* radix tree not lockless safe ! we use a brlock-type mecanism
588 * for now, until we can use a lockless radix tree
589 */
590static void irq_radix_wrlock(unsigned long *flags)
591{
592 unsigned int cpu, ok;
593
594 spin_lock_irqsave(&irq_big_lock, *flags);
595 irq_radix_writer = 1;
596 smp_mb();
597 do {
598 barrier();
599 ok = 1;
600 for_each_possible_cpu(cpu) {
601 if (per_cpu(irq_radix_reader, cpu)) {
602 ok = 0;
603 break;
604 }
605 }
606 if (!ok)
607 cpu_relax();
608 } while(!ok);
609}
610
611static void irq_radix_wrunlock(unsigned long flags)
612{
613 smp_wmb();
614 irq_radix_writer = 0;
615 spin_unlock_irqrestore(&irq_big_lock, flags);
616}
617
618static void irq_radix_rdlock(unsigned long *flags)
619{
620 local_irq_save(*flags);
621 __get_cpu_var(irq_radix_reader) = 1;
622 smp_mb();
623 if (likely(irq_radix_writer == 0))
624 return;
625 __get_cpu_var(irq_radix_reader) = 0;
626 smp_wmb();
627 spin_lock(&irq_big_lock);
628 __get_cpu_var(irq_radix_reader) = 1;
629 spin_unlock(&irq_big_lock);
630}
631
632static void irq_radix_rdunlock(unsigned long flags)
633{
634 __get_cpu_var(irq_radix_reader) = 0;
635 local_irq_restore(flags);
636}
637
638static int irq_setup_virq(struct irq_host *host, unsigned int virq, 586static int irq_setup_virq(struct irq_host *host, unsigned int virq,
639 irq_hw_number_t hwirq) 587 irq_hw_number_t hwirq)
640{ 588{
@@ -789,7 +737,6 @@ void irq_dispose_mapping(unsigned int virq)
789{ 737{
790 struct irq_host *host; 738 struct irq_host *host;
791 irq_hw_number_t hwirq; 739 irq_hw_number_t hwirq;
792 unsigned long flags;
793 740
794 if (virq == NO_IRQ) 741 if (virq == NO_IRQ)
795 return; 742 return;
@@ -829,9 +776,9 @@ void irq_dispose_mapping(unsigned int virq)
829 smp_rmb(); 776 smp_rmb();
830 if (revmap_trees_allocated < 1) 777 if (revmap_trees_allocated < 1)
831 break; 778 break;
832 irq_radix_wrlock(&flags); 779 mutex_lock(&revmap_trees_mutex);
833 radix_tree_delete(&host->revmap_data.tree, hwirq); 780 radix_tree_delete(&host->revmap_data.tree, hwirq);
834 irq_radix_wrunlock(flags); 781 mutex_unlock(&revmap_trees_mutex);
835 break; 782 break;
836 } 783 }
837 784
@@ -885,7 +832,6 @@ unsigned int irq_radix_revmap_lookup(struct irq_host *host,
885{ 832{
886 struct irq_map_entry *ptr; 833 struct irq_map_entry *ptr;
887 unsigned int virq; 834 unsigned int virq;
888 unsigned long flags;
889 835
890 WARN_ON(host->revmap_type != IRQ_HOST_MAP_TREE); 836 WARN_ON(host->revmap_type != IRQ_HOST_MAP_TREE);
891 837
@@ -897,9 +843,11 @@ unsigned int irq_radix_revmap_lookup(struct irq_host *host,
897 return irq_find_mapping(host, hwirq); 843 return irq_find_mapping(host, hwirq);
898 844
899 /* Now try to resolve */ 845 /* Now try to resolve */
900 irq_radix_rdlock(&flags); 846 /*
847 * No rcu_read_lock(ing) needed, the ptr returned can't go under us
848 * as it's referencing an entry in the static irq_map table.
849 */
901 ptr = radix_tree_lookup(&host->revmap_data.tree, hwirq); 850 ptr = radix_tree_lookup(&host->revmap_data.tree, hwirq);
902 irq_radix_rdunlock(flags);
903 851
904 /* 852 /*
905 * If found in radix tree, then fine. 853 * If found in radix tree, then fine.
@@ -917,7 +865,6 @@ unsigned int irq_radix_revmap_lookup(struct irq_host *host,
917void irq_radix_revmap_insert(struct irq_host *host, unsigned int virq, 865void irq_radix_revmap_insert(struct irq_host *host, unsigned int virq,
918 irq_hw_number_t hwirq) 866 irq_hw_number_t hwirq)
919{ 867{
920 unsigned long flags;
921 868
922 WARN_ON(host->revmap_type != IRQ_HOST_MAP_TREE); 869 WARN_ON(host->revmap_type != IRQ_HOST_MAP_TREE);
923 870
@@ -931,10 +878,10 @@ void irq_radix_revmap_insert(struct irq_host *host, unsigned int virq,
931 return; 878 return;
932 879
933 if (virq != NO_IRQ) { 880 if (virq != NO_IRQ) {
934 irq_radix_wrlock(&flags); 881 mutex_lock(&revmap_trees_mutex);
935 radix_tree_insert(&host->revmap_data.tree, hwirq, 882 radix_tree_insert(&host->revmap_data.tree, hwirq,
936 &irq_map[virq]); 883 &irq_map[virq]);
937 irq_radix_wrunlock(flags); 884 mutex_unlock(&revmap_trees_mutex);
938 } 885 }
939} 886}
940 887
@@ -1044,7 +991,6 @@ void irq_early_init(void)
1044static int irq_late_init(void) 991static int irq_late_init(void)
1045{ 992{
1046 struct irq_host *h; 993 struct irq_host *h;
1047 unsigned long flags;
1048 unsigned int i; 994 unsigned int i;
1049 995
1050 /* 996 /*
@@ -1068,14 +1014,14 @@ static int irq_late_init(void)
1068 * Insert the reverse mapping for those interrupts already present 1014 * Insert the reverse mapping for those interrupts already present
1069 * in irq_map[]. 1015 * in irq_map[].
1070 */ 1016 */
1071 irq_radix_wrlock(&flags); 1017 mutex_lock(&revmap_trees_mutex);
1072 for (i = 0; i < irq_virq_count; i++) { 1018 for (i = 0; i < irq_virq_count; i++) {
1073 if (irq_map[i].host && 1019 if (irq_map[i].host &&
1074 (irq_map[i].host->revmap_type == IRQ_HOST_MAP_TREE)) 1020 (irq_map[i].host->revmap_type == IRQ_HOST_MAP_TREE))
1075 radix_tree_insert(&irq_map[i].host->revmap_data.tree, 1021 radix_tree_insert(&irq_map[i].host->revmap_data.tree,
1076 irq_map[i].hwirq, &irq_map[i]); 1022 irq_map[i].hwirq, &irq_map[i]);
1077 } 1023 }
1078 irq_radix_wrunlock(flags); 1024 mutex_unlock(&revmap_trees_mutex);
1079 1025
1080 /* 1026 /*
1081 * Make sure the radix trees insertions are visible before setting 1027 * Make sure the radix trees insertions are visible before setting