diff options
author | Thomas Gleixner <tglx@linutronix.de> | 2012-07-16 06:42:39 -0400 |
---|---|---|
committer | Thomas Gleixner <tglx@linutronix.de> | 2012-08-13 11:01:08 -0400 |
commit | 81942621bd6b70b1a1ac4692b3f8f3be65a91b44 (patch) | |
tree | fe5155456b7677d30bbbb801fe280e3e98bed445 /drivers/infiniband/hw/ehca | |
parent | 62ab7072476ae1600e877cc62b43758e485f4f1e (diff) |
infiniband: Ehca: Use hotplug thread infrastructure
Get rid of the hotplug notifiers and use the generic hotplug thread
infrastructure.
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Srivatsa S. Bhat <srivatsa.bhat@linux.vnet.ibm.com>
Cc: Rusty Russell <rusty@rustcorp.com.au>
Reviewed-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Link: http://lkml.kernel.org/r/20120716103948.775527032@linutronix.de
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Diffstat (limited to 'drivers/infiniband/hw/ehca')
-rw-r--r-- | drivers/infiniband/hw/ehca/ehca_irq.c | 250 | ||||
-rw-r--r-- | drivers/infiniband/hw/ehca/ehca_irq.h | 6 |
2 files changed, 92 insertions, 164 deletions
diff --git a/drivers/infiniband/hw/ehca/ehca_irq.c b/drivers/infiniband/hw/ehca/ehca_irq.c index 53589000fd07..4eeac40cd943 100644 --- a/drivers/infiniband/hw/ehca/ehca_irq.c +++ b/drivers/infiniband/hw/ehca/ehca_irq.c | |||
@@ -42,6 +42,7 @@ | |||
42 | */ | 42 | */ |
43 | 43 | ||
44 | #include <linux/slab.h> | 44 | #include <linux/slab.h> |
45 | #include <linux/smpboot.h> | ||
45 | 46 | ||
46 | #include "ehca_classes.h" | 47 | #include "ehca_classes.h" |
47 | #include "ehca_irq.h" | 48 | #include "ehca_irq.h" |
@@ -652,7 +653,7 @@ void ehca_tasklet_eq(unsigned long data) | |||
652 | ehca_process_eq((struct ehca_shca*)data, 1); | 653 | ehca_process_eq((struct ehca_shca*)data, 1); |
653 | } | 654 | } |
654 | 655 | ||
655 | static inline int find_next_online_cpu(struct ehca_comp_pool *pool) | 656 | static int find_next_online_cpu(struct ehca_comp_pool *pool) |
656 | { | 657 | { |
657 | int cpu; | 658 | int cpu; |
658 | unsigned long flags; | 659 | unsigned long flags; |
@@ -662,17 +663,20 @@ static inline int find_next_online_cpu(struct ehca_comp_pool *pool) | |||
662 | ehca_dmp(cpu_online_mask, cpumask_size(), ""); | 663 | ehca_dmp(cpu_online_mask, cpumask_size(), ""); |
663 | 664 | ||
664 | spin_lock_irqsave(&pool->last_cpu_lock, flags); | 665 | spin_lock_irqsave(&pool->last_cpu_lock, flags); |
665 | cpu = cpumask_next(pool->last_cpu, cpu_online_mask); | 666 | do { |
666 | if (cpu >= nr_cpu_ids) | 667 | cpu = cpumask_next(pool->last_cpu, cpu_online_mask); |
667 | cpu = cpumask_first(cpu_online_mask); | 668 | if (cpu >= nr_cpu_ids) |
668 | pool->last_cpu = cpu; | 669 | cpu = cpumask_first(cpu_online_mask); |
670 | pool->last_cpu = cpu; | ||
671 | } while (!per_cpu_ptr(pool->cpu_comp_tasks, cpu)->active) | ||
669 | spin_unlock_irqrestore(&pool->last_cpu_lock, flags); | 672 | spin_unlock_irqrestore(&pool->last_cpu_lock, flags); |
670 | 673 | ||
671 | return cpu; | 674 | return cpu; |
672 | } | 675 | } |
673 | 676 | ||
674 | static void __queue_comp_task(struct ehca_cq *__cq, | 677 | static void __queue_comp_task(struct ehca_cq *__cq, |
675 | struct ehca_cpu_comp_task *cct) | 678 | struct ehca_cpu_comp_task *cct, |
679 | struct task_struct *thread) | ||
676 | { | 680 | { |
677 | unsigned long flags; | 681 | unsigned long flags; |
678 | 682 | ||
@@ -683,7 +687,7 @@ static void __queue_comp_task(struct ehca_cq *__cq, | |||
683 | __cq->nr_callbacks++; | 687 | __cq->nr_callbacks++; |
684 | list_add_tail(&__cq->entry, &cct->cq_list); | 688 | list_add_tail(&__cq->entry, &cct->cq_list); |
685 | cct->cq_jobs++; | 689 | cct->cq_jobs++; |
686 | wake_up(&cct->wait_queue); | 690 | wake_up_process(thread); |
687 | } else | 691 | } else |
688 | __cq->nr_callbacks++; | 692 | __cq->nr_callbacks++; |
689 | 693 | ||
@@ -695,6 +699,7 @@ static void queue_comp_task(struct ehca_cq *__cq) | |||
695 | { | 699 | { |
696 | int cpu_id; | 700 | int cpu_id; |
697 | struct ehca_cpu_comp_task *cct; | 701 | struct ehca_cpu_comp_task *cct; |
702 | struct task_struct *thread; | ||
698 | int cq_jobs; | 703 | int cq_jobs; |
699 | unsigned long flags; | 704 | unsigned long flags; |
700 | 705 | ||
@@ -702,7 +707,8 @@ static void queue_comp_task(struct ehca_cq *__cq) | |||
702 | BUG_ON(!cpu_online(cpu_id)); | 707 | BUG_ON(!cpu_online(cpu_id)); |
703 | 708 | ||
704 | cct = per_cpu_ptr(pool->cpu_comp_tasks, cpu_id); | 709 | cct = per_cpu_ptr(pool->cpu_comp_tasks, cpu_id); |
705 | BUG_ON(!cct); | 710 | thread = per_cpu_ptr(pool->cpu_comp_threads, cpu_id); |
711 | BUG_ON(!cct || !thread); | ||
706 | 712 | ||
707 | spin_lock_irqsave(&cct->task_lock, flags); | 713 | spin_lock_irqsave(&cct->task_lock, flags); |
708 | cq_jobs = cct->cq_jobs; | 714 | cq_jobs = cct->cq_jobs; |
@@ -710,28 +716,25 @@ static void queue_comp_task(struct ehca_cq *__cq) | |||
710 | if (cq_jobs > 0) { | 716 | if (cq_jobs > 0) { |
711 | cpu_id = find_next_online_cpu(pool); | 717 | cpu_id = find_next_online_cpu(pool); |
712 | cct = per_cpu_ptr(pool->cpu_comp_tasks, cpu_id); | 718 | cct = per_cpu_ptr(pool->cpu_comp_tasks, cpu_id); |
713 | BUG_ON(!cct); | 719 | thread = per_cpu_ptr(pool->cpu_comp_threads, cpu_id); |
720 | BUG_ON(!cct || !thread); | ||
714 | } | 721 | } |
715 | 722 | __queue_comp_task(__cq, cct, thread); | |
716 | __queue_comp_task(__cq, cct); | ||
717 | } | 723 | } |
718 | 724 | ||
719 | static void run_comp_task(struct ehca_cpu_comp_task *cct) | 725 | static void run_comp_task(struct ehca_cpu_comp_task *cct) |
720 | { | 726 | { |
721 | struct ehca_cq *cq; | 727 | struct ehca_cq *cq; |
722 | unsigned long flags; | ||
723 | |||
724 | spin_lock_irqsave(&cct->task_lock, flags); | ||
725 | 728 | ||
726 | while (!list_empty(&cct->cq_list)) { | 729 | while (!list_empty(&cct->cq_list)) { |
727 | cq = list_entry(cct->cq_list.next, struct ehca_cq, entry); | 730 | cq = list_entry(cct->cq_list.next, struct ehca_cq, entry); |
728 | spin_unlock_irqrestore(&cct->task_lock, flags); | 731 | spin_unlock_irq(&cct->task_lock); |
729 | 732 | ||
730 | comp_event_callback(cq); | 733 | comp_event_callback(cq); |
731 | if (atomic_dec_and_test(&cq->nr_events)) | 734 | if (atomic_dec_and_test(&cq->nr_events)) |
732 | wake_up(&cq->wait_completion); | 735 | wake_up(&cq->wait_completion); |
733 | 736 | ||
734 | spin_lock_irqsave(&cct->task_lock, flags); | 737 | spin_lock_irq(&cct->task_lock); |
735 | spin_lock(&cq->task_lock); | 738 | spin_lock(&cq->task_lock); |
736 | cq->nr_callbacks--; | 739 | cq->nr_callbacks--; |
737 | if (!cq->nr_callbacks) { | 740 | if (!cq->nr_callbacks) { |
@@ -740,159 +743,76 @@ static void run_comp_task(struct ehca_cpu_comp_task *cct) | |||
740 | } | 743 | } |
741 | spin_unlock(&cq->task_lock); | 744 | spin_unlock(&cq->task_lock); |
742 | } | 745 | } |
743 | |||
744 | spin_unlock_irqrestore(&cct->task_lock, flags); | ||
745 | } | 746 | } |
746 | 747 | ||
747 | static int comp_task(void *__cct) | 748 | static void comp_task_park(unsigned int cpu) |
748 | { | 749 | { |
749 | struct ehca_cpu_comp_task *cct = __cct; | 750 | struct ehca_cpu_comp_task *cct = per_cpu_ptr(pool->cpu_comp_tasks, cpu); |
750 | int cql_empty; | 751 | struct ehca_cpu_comp_task *target; |
751 | DECLARE_WAITQUEUE(wait, current); | 752 | struct task_struct *thread; |
752 | 753 | struct ehca_cq *cq, *tmp; | |
753 | set_current_state(TASK_INTERRUPTIBLE); | 754 | LIST_HEAD(list); |
754 | while (!kthread_should_stop()) { | ||
755 | add_wait_queue(&cct->wait_queue, &wait); | ||
756 | |||
757 | spin_lock_irq(&cct->task_lock); | ||
758 | cql_empty = list_empty(&cct->cq_list); | ||
759 | spin_unlock_irq(&cct->task_lock); | ||
760 | if (cql_empty) | ||
761 | schedule(); | ||
762 | else | ||
763 | __set_current_state(TASK_RUNNING); | ||
764 | |||
765 | remove_wait_queue(&cct->wait_queue, &wait); | ||
766 | 755 | ||
767 | spin_lock_irq(&cct->task_lock); | 756 | spin_lock_irq(&cct->task_lock); |
768 | cql_empty = list_empty(&cct->cq_list); | 757 | cct->cq_jobs = 0; |
769 | spin_unlock_irq(&cct->task_lock); | 758 | cct->active = 0; |
770 | if (!cql_empty) | 759 | list_splice_init(&cct->cq_list, &list); |
771 | run_comp_task(__cct); | 760 | spin_unlock_irq(&cct->task_lock); |
772 | 761 | ||
773 | set_current_state(TASK_INTERRUPTIBLE); | 762 | cpu = find_next_online_cpu(pool); |
763 | target = per_cpu_ptr(pool->cpu_comp_tasks, cpu); | ||
764 | thread = per_cpu_ptr(pool->cpu_comp_threads, cpu); | ||
765 | spin_lock_irq(&target->task_lock); | ||
766 | list_for_each_entry_safe(cq, tmp, &list, entry) { | ||
767 | list_del(&cq->entry); | ||
768 | __queue_comp_task(cq, target, thread); | ||
774 | } | 769 | } |
775 | __set_current_state(TASK_RUNNING); | 770 | spin_unlock_irq(&target->task_lock); |
776 | |||
777 | return 0; | ||
778 | } | ||
779 | |||
780 | static struct task_struct *create_comp_task(struct ehca_comp_pool *pool, | ||
781 | int cpu) | ||
782 | { | ||
783 | struct ehca_cpu_comp_task *cct; | ||
784 | |||
785 | cct = per_cpu_ptr(pool->cpu_comp_tasks, cpu); | ||
786 | spin_lock_init(&cct->task_lock); | ||
787 | INIT_LIST_HEAD(&cct->cq_list); | ||
788 | init_waitqueue_head(&cct->wait_queue); | ||
789 | cct->task = kthread_create_on_node(comp_task, cct, cpu_to_node(cpu), | ||
790 | "ehca_comp/%d", cpu); | ||
791 | |||
792 | return cct->task; | ||
793 | } | 771 | } |
794 | 772 | ||
795 | static void destroy_comp_task(struct ehca_comp_pool *pool, | 773 | static void comp_task_stop(unsigned int cpu, bool online) |
796 | int cpu) | ||
797 | { | 774 | { |
798 | struct ehca_cpu_comp_task *cct; | 775 | struct ehca_cpu_comp_task *cct = per_cpu_ptr(pool->cpu_comp_tasks, cpu); |
799 | struct task_struct *task; | ||
800 | unsigned long flags_cct; | ||
801 | |||
802 | cct = per_cpu_ptr(pool->cpu_comp_tasks, cpu); | ||
803 | |||
804 | spin_lock_irqsave(&cct->task_lock, flags_cct); | ||
805 | 776 | ||
806 | task = cct->task; | 777 | spin_lock_irq(&cct->task_lock); |
807 | cct->task = NULL; | ||
808 | cct->cq_jobs = 0; | 778 | cct->cq_jobs = 0; |
809 | 779 | cct->active = 0; | |
810 | spin_unlock_irqrestore(&cct->task_lock, flags_cct); | 780 | WARN_ON(!list_empty(&cct->cq_list)); |
811 | 781 | spin_unlock_irq(&cct->task_lock); | |
812 | if (task) | ||
813 | kthread_stop(task); | ||
814 | } | 782 | } |
815 | 783 | ||
816 | static void __cpuinit take_over_work(struct ehca_comp_pool *pool, int cpu) | 784 | static int comp_task_should_run(unsigned int cpu) |
817 | { | 785 | { |
818 | struct ehca_cpu_comp_task *cct = per_cpu_ptr(pool->cpu_comp_tasks, cpu); | 786 | struct ehca_cpu_comp_task *cct = per_cpu_ptr(pool->cpu_comp_tasks, cpu); |
819 | LIST_HEAD(list); | ||
820 | struct ehca_cq *cq; | ||
821 | unsigned long flags_cct; | ||
822 | |||
823 | spin_lock_irqsave(&cct->task_lock, flags_cct); | ||
824 | |||
825 | list_splice_init(&cct->cq_list, &list); | ||
826 | |||
827 | while (!list_empty(&list)) { | ||
828 | cq = list_entry(cct->cq_list.next, struct ehca_cq, entry); | ||
829 | |||
830 | list_del(&cq->entry); | ||
831 | __queue_comp_task(cq, this_cpu_ptr(pool->cpu_comp_tasks)); | ||
832 | } | ||
833 | |||
834 | spin_unlock_irqrestore(&cct->task_lock, flags_cct); | ||
835 | 787 | ||
788 | return cct->cq_jobs; | ||
836 | } | 789 | } |
837 | 790 | ||
838 | static int __cpuinit comp_pool_callback(struct notifier_block *nfb, | 791 | static int comp_task(unsigned int cpu) |
839 | unsigned long action, | ||
840 | void *hcpu) | ||
841 | { | 792 | { |
842 | unsigned int cpu = (unsigned long)hcpu; | 793 | struct ehca_cpu_comp_task *cct = this_cpu_ptr(pool->cpu_comp_tasks); |
843 | struct ehca_cpu_comp_task *cct; | 794 | int cql_empty; |
844 | 795 | ||
845 | switch (action) { | 796 | spin_lock_irq(&cct->task_lock); |
846 | case CPU_UP_PREPARE: | 797 | cql_empty = list_empty(&cct->cq_list); |
847 | case CPU_UP_PREPARE_FROZEN: | 798 | if (!cql_empty) { |
848 | ehca_gen_dbg("CPU: %x (CPU_PREPARE)", cpu); | 799 | __set_current_state(TASK_RUNNING); |
849 | if (!create_comp_task(pool, cpu)) { | 800 | run_comp_task(cct); |
850 | ehca_gen_err("Can't create comp_task for cpu: %x", cpu); | ||
851 | return notifier_from_errno(-ENOMEM); | ||
852 | } | ||
853 | break; | ||
854 | case CPU_UP_CANCELED: | ||
855 | case CPU_UP_CANCELED_FROZEN: | ||
856 | ehca_gen_dbg("CPU: %x (CPU_CANCELED)", cpu); | ||
857 | cct = per_cpu_ptr(pool->cpu_comp_tasks, cpu); | ||
858 | kthread_bind(cct->task, cpumask_any(cpu_online_mask)); | ||
859 | destroy_comp_task(pool, cpu); | ||
860 | break; | ||
861 | case CPU_ONLINE: | ||
862 | case CPU_ONLINE_FROZEN: | ||
863 | ehca_gen_dbg("CPU: %x (CPU_ONLINE)", cpu); | ||
864 | cct = per_cpu_ptr(pool->cpu_comp_tasks, cpu); | ||
865 | kthread_bind(cct->task, cpu); | ||
866 | wake_up_process(cct->task); | ||
867 | break; | ||
868 | case CPU_DOWN_PREPARE: | ||
869 | case CPU_DOWN_PREPARE_FROZEN: | ||
870 | ehca_gen_dbg("CPU: %x (CPU_DOWN_PREPARE)", cpu); | ||
871 | break; | ||
872 | case CPU_DOWN_FAILED: | ||
873 | case CPU_DOWN_FAILED_FROZEN: | ||
874 | ehca_gen_dbg("CPU: %x (CPU_DOWN_FAILED)", cpu); | ||
875 | break; | ||
876 | case CPU_DEAD: | ||
877 | case CPU_DEAD_FROZEN: | ||
878 | ehca_gen_dbg("CPU: %x (CPU_DEAD)", cpu); | ||
879 | destroy_comp_task(pool, cpu); | ||
880 | take_over_work(pool, cpu); | ||
881 | break; | ||
882 | } | 801 | } |
883 | 802 | spin_unlock_irq(&cct->task_lock); | |
884 | return NOTIFY_OK; | ||
885 | } | 803 | } |
886 | 804 | ||
887 | static struct notifier_block comp_pool_callback_nb __cpuinitdata = { | 805 | static struct smp_hotplug_thread comp_pool_threads = { |
888 | .notifier_call = comp_pool_callback, | 806 | .thread_should_run = comp_task_should_run, |
889 | .priority = 0, | 807 | .thread_fn = comp_task, |
808 | .thread_comm = "ehca_comp/%u", | ||
809 | .cleanup = comp_task_stop, | ||
810 | .park = comp_task_park, | ||
890 | }; | 811 | }; |
891 | 812 | ||
892 | int ehca_create_comp_pool(void) | 813 | int ehca_create_comp_pool(void) |
893 | { | 814 | { |
894 | int cpu; | 815 | int cpu, ret = -ENOMEM; |
895 | struct task_struct *task; | ||
896 | 816 | ||
897 | if (!ehca_scaling_code) | 817 | if (!ehca_scaling_code) |
898 | return 0; | 818 | return 0; |
@@ -905,38 +825,46 @@ int ehca_create_comp_pool(void) | |||
905 | pool->last_cpu = cpumask_any(cpu_online_mask); | 825 | pool->last_cpu = cpumask_any(cpu_online_mask); |
906 | 826 | ||
907 | pool->cpu_comp_tasks = alloc_percpu(struct ehca_cpu_comp_task); | 827 | pool->cpu_comp_tasks = alloc_percpu(struct ehca_cpu_comp_task); |
908 | if (pool->cpu_comp_tasks == NULL) { | 828 | if (!pool->cpu_comp_tasks) |
909 | kfree(pool); | 829 | goto out_pool; |
910 | return -EINVAL; | ||
911 | } | ||
912 | 830 | ||
913 | for_each_online_cpu(cpu) { | 831 | pool->cpu_comp_threads = alloc_percpu(struct task_struct *); |
914 | task = create_comp_task(pool, cpu); | 832 | if (!pool->cpu_comp_threads) |
915 | if (task) { | 833 | goto out_tasks; |
916 | kthread_bind(task, cpu); | 834 | |
917 | wake_up_process(task); | 835 | for_each_present_cpu(cpu) { |
918 | } | 836 | struct ehca_cpu_comp_task *cct; |
837 | |||
838 | cct = per_cpu_ptr(pool->cpu_comp_tasks, cpu); | ||
839 | spin_lock_init(&cct->task_lock); | ||
840 | INIT_LIST_HEAD(&cct->cq_list); | ||
919 | } | 841 | } |
920 | 842 | ||
921 | register_hotcpu_notifier(&comp_pool_callback_nb); | 843 | comp_pool_threads.store = pool->cpu_comp_threads; |
844 | ret = smpboot_register_percpu_thread(&comp_pool_threads); | ||
845 | if (ret) | ||
846 | goto out_threads; | ||
922 | 847 | ||
923 | printk(KERN_INFO "eHCA scaling code enabled\n"); | 848 | pr_info("eHCA scaling code enabled\n"); |
849 | return ret; | ||
924 | 850 | ||
925 | return 0; | 851 | out_threads: |
852 | free_percpu(pool->cpu_comp_threads); | ||
853 | out_tasks: | ||
854 | free_percpu(pool->cpu_comp_tasks); | ||
855 | out_pool: | ||
856 | kfree(pool); | ||
857 | return ret; | ||
926 | } | 858 | } |
927 | 859 | ||
928 | void ehca_destroy_comp_pool(void) | 860 | void ehca_destroy_comp_pool(void) |
929 | { | 861 | { |
930 | int i; | ||
931 | |||
932 | if (!ehca_scaling_code) | 862 | if (!ehca_scaling_code) |
933 | return; | 863 | return; |
934 | 864 | ||
935 | unregister_hotcpu_notifier(&comp_pool_callback_nb); | 865 | smpboot_unregister_percpu_thread(&comp_pool_threads); |
936 | |||
937 | for_each_online_cpu(i) | ||
938 | destroy_comp_task(pool, i); | ||
939 | 866 | ||
867 | free_percpu(pool->cpu_comp_threads); | ||
940 | free_percpu(pool->cpu_comp_tasks); | 868 | free_percpu(pool->cpu_comp_tasks); |
941 | kfree(pool); | 869 | kfree(pool); |
942 | } | 870 | } |
diff --git a/drivers/infiniband/hw/ehca/ehca_irq.h b/drivers/infiniband/hw/ehca/ehca_irq.h index 3346cb06cea6..5370199f08c7 100644 --- a/drivers/infiniband/hw/ehca/ehca_irq.h +++ b/drivers/infiniband/hw/ehca/ehca_irq.h | |||
@@ -58,15 +58,15 @@ void ehca_tasklet_eq(unsigned long data); | |||
58 | void ehca_process_eq(struct ehca_shca *shca, int is_irq); | 58 | void ehca_process_eq(struct ehca_shca *shca, int is_irq); |
59 | 59 | ||
60 | struct ehca_cpu_comp_task { | 60 | struct ehca_cpu_comp_task { |
61 | wait_queue_head_t wait_queue; | ||
62 | struct list_head cq_list; | 61 | struct list_head cq_list; |
63 | struct task_struct *task; | ||
64 | spinlock_t task_lock; | 62 | spinlock_t task_lock; |
65 | int cq_jobs; | 63 | int cq_jobs; |
64 | int active; | ||
66 | }; | 65 | }; |
67 | 66 | ||
68 | struct ehca_comp_pool { | 67 | struct ehca_comp_pool { |
69 | struct ehca_cpu_comp_task *cpu_comp_tasks; | 68 | struct ehca_cpu_comp_task __percpu *cpu_comp_tasks; |
69 | struct task_struct * __percpu *cpu_comp_threads; | ||
70 | int last_cpu; | 70 | int last_cpu; |
71 | spinlock_t last_cpu_lock; | 71 | spinlock_t last_cpu_lock; |
72 | }; | 72 | }; |