diff options
-rw-r--r-- | arch/powerpc/include/asm/irq.h | 18 | ||||
-rw-r--r-- | arch/powerpc/kernel/irq.c | 97 | ||||
-rw-r--r-- | arch/powerpc/platforms/pseries/xics.c | 11 |
3 files changed, 95 insertions, 31 deletions
diff --git a/arch/powerpc/include/asm/irq.h b/arch/powerpc/include/asm/irq.h index a372f76836c2..0a5137676e1b 100644 --- a/arch/powerpc/include/asm/irq.h +++ b/arch/powerpc/include/asm/irq.h | |||
@@ -236,15 +236,27 @@ extern unsigned int irq_find_mapping(struct irq_host *host, | |||
236 | extern unsigned int irq_create_direct_mapping(struct irq_host *host); | 236 | extern unsigned int irq_create_direct_mapping(struct irq_host *host); |
237 | 237 | ||
238 | /** | 238 | /** |
239 | * irq_radix_revmap - Find a linux virq from a hw irq number. | 239 | * irq_radix_revmap_insert - Insert a hw irq to linux virq number mapping. |
240 | * @host: host owning this hardware interrupt | ||
241 | * @virq: linux irq number | ||
242 | * @hwirq: hardware irq number in that host space | ||
243 | * | ||
244 | * This is for use by irq controllers that use a radix tree reverse | ||
245 | * mapping for fast lookup. | ||
246 | */ | ||
247 | extern void irq_radix_revmap_insert(struct irq_host *host, unsigned int virq, | ||
248 | irq_hw_number_t hwirq); | ||
249 | |||
250 | /** | ||
251 | * irq_radix_revmap_lookup - Find a linux virq from a hw irq number. | ||
240 | * @host: host owning this hardware interrupt | 252 | * @host: host owning this hardware interrupt |
241 | * @hwirq: hardware irq number in that host space | 253 | * @hwirq: hardware irq number in that host space |
242 | * | 254 | * |
243 | * This is a fast path, for use by irq controller code that uses radix tree | 255 | * This is a fast path, for use by irq controller code that uses radix tree |
244 | * revmaps | 256 | * revmaps |
245 | */ | 257 | */ |
246 | extern unsigned int irq_radix_revmap(struct irq_host *host, | 258 | extern unsigned int irq_radix_revmap_lookup(struct irq_host *host, |
247 | irq_hw_number_t hwirq); | 259 | irq_hw_number_t hwirq); |
248 | 260 | ||
249 | /** | 261 | /** |
250 | * irq_linear_revmap - Find a linux virq from a hw irq number. | 262 | * irq_linear_revmap - Find a linux virq from a hw irq number. |
diff --git a/arch/powerpc/kernel/irq.c b/arch/powerpc/kernel/irq.c index d972decf0324..2656924415da 100644 --- a/arch/powerpc/kernel/irq.c +++ b/arch/powerpc/kernel/irq.c | |||
@@ -441,6 +441,7 @@ static LIST_HEAD(irq_hosts); | |||
441 | static DEFINE_SPINLOCK(irq_big_lock); | 441 | static DEFINE_SPINLOCK(irq_big_lock); |
442 | static DEFINE_PER_CPU(unsigned int, irq_radix_reader); | 442 | static DEFINE_PER_CPU(unsigned int, irq_radix_reader); |
443 | static unsigned int irq_radix_writer; | 443 | static unsigned int irq_radix_writer; |
444 | static unsigned int revmap_trees_allocated; | ||
444 | struct irq_map_entry irq_map[NR_IRQS]; | 445 | struct irq_map_entry irq_map[NR_IRQS]; |
445 | static unsigned int irq_virq_count = NR_IRQS; | 446 | static unsigned int irq_virq_count = NR_IRQS; |
446 | static struct irq_host *irq_default_host; | 447 | static struct irq_host *irq_default_host; |
@@ -821,8 +822,12 @@ void irq_dispose_mapping(unsigned int virq) | |||
821 | host->revmap_data.linear.revmap[hwirq] = NO_IRQ; | 822 | host->revmap_data.linear.revmap[hwirq] = NO_IRQ; |
822 | break; | 823 | break; |
823 | case IRQ_HOST_MAP_TREE: | 824 | case IRQ_HOST_MAP_TREE: |
824 | /* Check if radix tree allocated yet */ | 825 | /* |
825 | if (host->revmap_data.tree.gfp_mask == 0) | 826 | * Check if radix tree allocated yet, if not then nothing to |
827 | * remove. | ||
828 | */ | ||
829 | smp_rmb(); | ||
830 | if (revmap_trees_allocated < 1) | ||
826 | break; | 831 | break; |
827 | irq_radix_wrlock(&flags); | 832 | irq_radix_wrlock(&flags); |
828 | radix_tree_delete(&host->revmap_data.tree, hwirq); | 833 | radix_tree_delete(&host->revmap_data.tree, hwirq); |
@@ -875,43 +880,62 @@ unsigned int irq_find_mapping(struct irq_host *host, | |||
875 | EXPORT_SYMBOL_GPL(irq_find_mapping); | 880 | EXPORT_SYMBOL_GPL(irq_find_mapping); |
876 | 881 | ||
877 | 882 | ||
878 | unsigned int irq_radix_revmap(struct irq_host *host, | 883 | unsigned int irq_radix_revmap_lookup(struct irq_host *host, |
879 | irq_hw_number_t hwirq) | 884 | irq_hw_number_t hwirq) |
880 | { | 885 | { |
881 | struct radix_tree_root *tree; | ||
882 | struct irq_map_entry *ptr; | 886 | struct irq_map_entry *ptr; |
883 | unsigned int virq; | 887 | unsigned int virq; |
884 | unsigned long flags; | 888 | unsigned long flags; |
885 | 889 | ||
886 | WARN_ON(host->revmap_type != IRQ_HOST_MAP_TREE); | 890 | WARN_ON(host->revmap_type != IRQ_HOST_MAP_TREE); |
887 | 891 | ||
888 | /* Check if the radix tree exist yet. We test the value of | 892 | /* |
889 | * the gfp_mask for that. Sneaky but saves another int in the | 893 | * Check if the radix tree exists and has bee initialized. |
890 | * structure. If not, we fallback to slow mode | 894 | * If not, we fallback to slow mode |
891 | */ | 895 | */ |
892 | tree = &host->revmap_data.tree; | 896 | if (revmap_trees_allocated < 2) |
893 | if (tree->gfp_mask == 0) | ||
894 | return irq_find_mapping(host, hwirq); | 897 | return irq_find_mapping(host, hwirq); |
895 | 898 | ||
896 | /* Now try to resolve */ | 899 | /* Now try to resolve */ |
897 | irq_radix_rdlock(&flags); | 900 | irq_radix_rdlock(&flags); |
898 | ptr = radix_tree_lookup(tree, hwirq); | 901 | ptr = radix_tree_lookup(&host->revmap_data.tree, hwirq); |
899 | irq_radix_rdunlock(flags); | 902 | irq_radix_rdunlock(flags); |
900 | 903 | ||
901 | /* Found it, return */ | 904 | /* |
902 | if (ptr) { | 905 | * If found in radix tree, then fine. |
906 | * Else fallback to linear lookup - this should not happen in practice | ||
907 | * as it means that we failed to insert the node in the radix tree. | ||
908 | */ | ||
909 | if (ptr) | ||
903 | virq = ptr - irq_map; | 910 | virq = ptr - irq_map; |
904 | return virq; | 911 | else |
905 | } | 912 | virq = irq_find_mapping(host, hwirq); |
913 | |||
914 | return virq; | ||
915 | } | ||
916 | |||
917 | void irq_radix_revmap_insert(struct irq_host *host, unsigned int virq, | ||
918 | irq_hw_number_t hwirq) | ||
919 | { | ||
920 | unsigned long flags; | ||
921 | |||
922 | WARN_ON(host->revmap_type != IRQ_HOST_MAP_TREE); | ||
923 | |||
924 | /* | ||
925 | * Check if the radix tree exists yet. | ||
926 | * If not, then the irq will be inserted into the tree when it gets | ||
927 | * initialized. | ||
928 | */ | ||
929 | smp_rmb(); | ||
930 | if (revmap_trees_allocated < 1) | ||
931 | return; | ||
906 | 932 | ||
907 | /* If not there, try to insert it */ | ||
908 | virq = irq_find_mapping(host, hwirq); | ||
909 | if (virq != NO_IRQ) { | 933 | if (virq != NO_IRQ) { |
910 | irq_radix_wrlock(&flags); | 934 | irq_radix_wrlock(&flags); |
911 | radix_tree_insert(tree, hwirq, &irq_map[virq]); | 935 | radix_tree_insert(&host->revmap_data.tree, hwirq, |
936 | &irq_map[virq]); | ||
912 | irq_radix_wrunlock(flags); | 937 | irq_radix_wrunlock(flags); |
913 | } | 938 | } |
914 | return virq; | ||
915 | } | 939 | } |
916 | 940 | ||
917 | unsigned int irq_linear_revmap(struct irq_host *host, | 941 | unsigned int irq_linear_revmap(struct irq_host *host, |
@@ -1021,14 +1045,45 @@ static int irq_late_init(void) | |||
1021 | { | 1045 | { |
1022 | struct irq_host *h; | 1046 | struct irq_host *h; |
1023 | unsigned long flags; | 1047 | unsigned long flags; |
1048 | unsigned int i; | ||
1024 | 1049 | ||
1025 | irq_radix_wrlock(&flags); | 1050 | /* |
1051 | * No mutual exclusion with respect to accessors of the tree is needed | ||
1052 | * here as the synchronization is done via the state variable | ||
1053 | * revmap_trees_allocated. | ||
1054 | */ | ||
1026 | list_for_each_entry(h, &irq_hosts, link) { | 1055 | list_for_each_entry(h, &irq_hosts, link) { |
1027 | if (h->revmap_type == IRQ_HOST_MAP_TREE) | 1056 | if (h->revmap_type == IRQ_HOST_MAP_TREE) |
1028 | INIT_RADIX_TREE(&h->revmap_data.tree, GFP_ATOMIC); | 1057 | INIT_RADIX_TREE(&h->revmap_data.tree, GFP_KERNEL); |
1058 | } | ||
1059 | |||
1060 | /* | ||
1061 | * Make sure the radix trees inits are visible before setting | ||
1062 | * the flag | ||
1063 | */ | ||
1064 | smp_wmb(); | ||
1065 | revmap_trees_allocated = 1; | ||
1066 | |||
1067 | /* | ||
1068 | * Insert the reverse mapping for those interrupts already present | ||
1069 | * in irq_map[]. | ||
1070 | */ | ||
1071 | irq_radix_wrlock(&flags); | ||
1072 | for (i = 0; i < irq_virq_count; i++) { | ||
1073 | if (irq_map[i].host && | ||
1074 | (irq_map[i].host->revmap_type == IRQ_HOST_MAP_TREE)) | ||
1075 | radix_tree_insert(&irq_map[i].host->revmap_data.tree, | ||
1076 | irq_map[i].hwirq, &irq_map[i]); | ||
1029 | } | 1077 | } |
1030 | irq_radix_wrunlock(flags); | 1078 | irq_radix_wrunlock(flags); |
1031 | 1079 | ||
1080 | /* | ||
1081 | * Make sure the radix trees insertions are visible before setting | ||
1082 | * the flag | ||
1083 | */ | ||
1084 | smp_wmb(); | ||
1085 | revmap_trees_allocated = 2; | ||
1086 | |||
1032 | return 0; | 1087 | return 0; |
1033 | } | 1088 | } |
1034 | arch_initcall(irq_late_init); | 1089 | arch_initcall(irq_late_init); |
diff --git a/arch/powerpc/platforms/pseries/xics.c b/arch/powerpc/platforms/pseries/xics.c index 0fc830f576f5..6b1a005cc0cc 100644 --- a/arch/powerpc/platforms/pseries/xics.c +++ b/arch/powerpc/platforms/pseries/xics.c | |||
@@ -310,12 +310,6 @@ static void xics_mask_irq(unsigned int virq) | |||
310 | 310 | ||
311 | static unsigned int xics_startup(unsigned int virq) | 311 | static unsigned int xics_startup(unsigned int virq) |
312 | { | 312 | { |
313 | unsigned int irq; | ||
314 | |||
315 | /* force a reverse mapping of the interrupt so it gets in the cache */ | ||
316 | irq = (unsigned int)irq_map[virq].hwirq; | ||
317 | irq_radix_revmap(xics_host, irq); | ||
318 | |||
319 | /* unmask it */ | 313 | /* unmask it */ |
320 | xics_unmask_irq(virq); | 314 | xics_unmask_irq(virq); |
321 | return 0; | 315 | return 0; |
@@ -346,7 +340,7 @@ static inline unsigned int xics_remap_irq(unsigned int vec) | |||
346 | 340 | ||
347 | if (vec == XICS_IRQ_SPURIOUS) | 341 | if (vec == XICS_IRQ_SPURIOUS) |
348 | return NO_IRQ; | 342 | return NO_IRQ; |
349 | irq = irq_radix_revmap(xics_host, vec); | 343 | irq = irq_radix_revmap_lookup(xics_host, vec); |
350 | if (likely(irq != NO_IRQ)) | 344 | if (likely(irq != NO_IRQ)) |
351 | return irq; | 345 | return irq; |
352 | 346 | ||
@@ -530,6 +524,9 @@ static int xics_host_map(struct irq_host *h, unsigned int virq, | |||
530 | { | 524 | { |
531 | pr_debug("xics: map virq %d, hwirq 0x%lx\n", virq, hw); | 525 | pr_debug("xics: map virq %d, hwirq 0x%lx\n", virq, hw); |
532 | 526 | ||
527 | /* Insert the interrupt mapping into the radix tree for fast lookup */ | ||
528 | irq_radix_revmap_insert(xics_host, virq, hw); | ||
529 | |||
533 | get_irq_desc(virq)->status |= IRQ_LEVEL; | 530 | get_irq_desc(virq)->status |= IRQ_LEVEL; |
534 | set_irq_chip_and_handler(virq, xics_irq_chip, handle_fasteoi_irq); | 531 | set_irq_chip_and_handler(virq, xics_irq_chip, handle_fasteoi_irq); |
535 | return 0; | 532 | return 0; |