diff options
Diffstat (limited to 'arch/powerpc/kernel/irq.c')
-rw-r--r-- | arch/powerpc/kernel/irq.c | 84 |
1 files changed, 66 insertions, 18 deletions
diff --git a/arch/powerpc/kernel/irq.c b/arch/powerpc/kernel/irq.c index fd4ddb858db..b4432332341 100644 --- a/arch/powerpc/kernel/irq.c +++ b/arch/powerpc/kernel/irq.c | |||
@@ -323,7 +323,8 @@ EXPORT_SYMBOL(do_softirq); | |||
323 | 323 | ||
324 | static LIST_HEAD(irq_hosts); | 324 | static LIST_HEAD(irq_hosts); |
325 | static spinlock_t irq_big_lock = SPIN_LOCK_UNLOCKED; | 325 | static spinlock_t irq_big_lock = SPIN_LOCK_UNLOCKED; |
326 | 326 | static DEFINE_PER_CPU(unsigned int, irq_radix_reader); | |
327 | static unsigned int irq_radix_writer; | ||
327 | struct irq_map_entry irq_map[NR_IRQS]; | 328 | struct irq_map_entry irq_map[NR_IRQS]; |
328 | static unsigned int irq_virq_count = NR_IRQS; | 329 | static unsigned int irq_virq_count = NR_IRQS; |
329 | static struct irq_host *irq_default_host; | 330 | static 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 | */ | ||
463 | static 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 | |||
484 | static 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 | |||
491 | static 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 | |||
505 | static void irq_radix_rdunlock(unsigned long flags) | ||
506 | { | ||
507 | __get_cpu_var(irq_radix_reader) = 0; | ||
508 | local_irq_restore(flags); | ||
509 | } | ||
510 | |||
511 | |||
459 | unsigned int irq_create_mapping(struct irq_host *host, | 512 | unsigned 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 | } |