aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/powerpc/include/asm/irq.h18
-rw-r--r--arch/powerpc/kernel/irq.c97
-rw-r--r--arch/powerpc/platforms/pseries/xics.c11
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,
236extern unsigned int irq_create_direct_mapping(struct irq_host *host); 236extern 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 */
247extern 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 */
246extern unsigned int irq_radix_revmap(struct irq_host *host, 258extern 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);
441static DEFINE_SPINLOCK(irq_big_lock); 441static DEFINE_SPINLOCK(irq_big_lock);
442static DEFINE_PER_CPU(unsigned int, irq_radix_reader); 442static DEFINE_PER_CPU(unsigned int, irq_radix_reader);
443static unsigned int irq_radix_writer; 443static unsigned int irq_radix_writer;
444static unsigned int revmap_trees_allocated;
444struct irq_map_entry irq_map[NR_IRQS]; 445struct irq_map_entry irq_map[NR_IRQS];
445static unsigned int irq_virq_count = NR_IRQS; 446static unsigned int irq_virq_count = NR_IRQS;
446static struct irq_host *irq_default_host; 447static 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,
875EXPORT_SYMBOL_GPL(irq_find_mapping); 880EXPORT_SYMBOL_GPL(irq_find_mapping);
876 881
877 882
878unsigned int irq_radix_revmap(struct irq_host *host, 883unsigned 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
917void 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
917unsigned int irq_linear_revmap(struct irq_host *host, 941unsigned 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}
1034arch_initcall(irq_late_init); 1089arch_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
311static unsigned int xics_startup(unsigned int virq) 311static 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;