diff options
Diffstat (limited to 'arch/ia64/kernel/smpboot.c')
-rw-r--r-- | arch/ia64/kernel/smpboot.c | 294 |
1 files changed, 261 insertions, 33 deletions
diff --git a/arch/ia64/kernel/smpboot.c b/arch/ia64/kernel/smpboot.c index 5318f0cbfc26..0d5ee57c9865 100644 --- a/arch/ia64/kernel/smpboot.c +++ b/arch/ia64/kernel/smpboot.c | |||
@@ -1,14 +1,25 @@ | |||
1 | /* | 1 | /* |
2 | * SMP boot-related support | 2 | * SMP boot-related support |
3 | * | 3 | * |
4 | * Copyright (C) 1998-2003 Hewlett-Packard Co | 4 | * Copyright (C) 1998-2003, 2005 Hewlett-Packard Co |
5 | * David Mosberger-Tang <davidm@hpl.hp.com> | 5 | * David Mosberger-Tang <davidm@hpl.hp.com> |
6 | * Copyright (C) 2001, 2004-2005 Intel Corp | ||
7 | * Rohit Seth <rohit.seth@intel.com> | ||
8 | * Suresh Siddha <suresh.b.siddha@intel.com> | ||
9 | * Gordon Jin <gordon.jin@intel.com> | ||
10 | * Ashok Raj <ashok.raj@intel.com> | ||
6 | * | 11 | * |
7 | * 01/05/16 Rohit Seth <rohit.seth@intel.com> Moved SMP booting functions from smp.c to here. | 12 | * 01/05/16 Rohit Seth <rohit.seth@intel.com> Moved SMP booting functions from smp.c to here. |
8 | * 01/04/27 David Mosberger <davidm@hpl.hp.com> Added ITC synching code. | 13 | * 01/04/27 David Mosberger <davidm@hpl.hp.com> Added ITC synching code. |
9 | * 02/07/31 David Mosberger <davidm@hpl.hp.com> Switch over to hotplug-CPU boot-sequence. | 14 | * 02/07/31 David Mosberger <davidm@hpl.hp.com> Switch over to hotplug-CPU boot-sequence. |
10 | * smp_boot_cpus()/smp_commence() is replaced by | 15 | * smp_boot_cpus()/smp_commence() is replaced by |
11 | * smp_prepare_cpus()/__cpu_up()/smp_cpus_done(). | 16 | * smp_prepare_cpus()/__cpu_up()/smp_cpus_done(). |
17 | * 04/06/21 Ashok Raj <ashok.raj@intel.com> Added CPU Hotplug Support | ||
18 | * 04/12/26 Jin Gordon <gordon.jin@intel.com> | ||
19 | * 04/12/26 Rohit Seth <rohit.seth@intel.com> | ||
20 | * Add multi-threading and multi-core detection | ||
21 | * 05/01/30 Suresh Siddha <suresh.b.siddha@intel.com> | ||
22 | * Setup cpu_sibling_map and cpu_core_map | ||
12 | */ | 23 | */ |
13 | #include <linux/config.h> | 24 | #include <linux/config.h> |
14 | 25 | ||
@@ -58,6 +69,37 @@ | |||
58 | #define Dprintk(x...) | 69 | #define Dprintk(x...) |
59 | #endif | 70 | #endif |
60 | 71 | ||
72 | #ifdef CONFIG_HOTPLUG_CPU | ||
73 | /* | ||
74 | * Store all idle threads, this can be reused instead of creating | ||
75 | * a new thread. Also avoids complicated thread destroy functionality | ||
76 | * for idle threads. | ||
77 | */ | ||
78 | struct task_struct *idle_thread_array[NR_CPUS]; | ||
79 | |||
80 | /* | ||
81 | * Global array allocated for NR_CPUS at boot time | ||
82 | */ | ||
83 | struct sal_to_os_boot sal_boot_rendez_state[NR_CPUS]; | ||
84 | |||
85 | /* | ||
86 | * start_ap in head.S uses this to store current booting cpu | ||
87 | * info. | ||
88 | */ | ||
89 | struct sal_to_os_boot *sal_state_for_booting_cpu = &sal_boot_rendez_state[0]; | ||
90 | |||
91 | #define set_brendez_area(x) (sal_state_for_booting_cpu = &sal_boot_rendez_state[(x)]); | ||
92 | |||
93 | #define get_idle_for_cpu(x) (idle_thread_array[(x)]) | ||
94 | #define set_idle_for_cpu(x,p) (idle_thread_array[(x)] = (p)) | ||
95 | |||
96 | #else | ||
97 | |||
98 | #define get_idle_for_cpu(x) (NULL) | ||
99 | #define set_idle_for_cpu(x,p) | ||
100 | #define set_brendez_area(x) | ||
101 | #endif | ||
102 | |||
61 | 103 | ||
62 | /* | 104 | /* |
63 | * ITC synchronization related stuff: | 105 | * ITC synchronization related stuff: |
@@ -90,6 +132,11 @@ EXPORT_SYMBOL(cpu_online_map); | |||
90 | cpumask_t cpu_possible_map; | 132 | cpumask_t cpu_possible_map; |
91 | EXPORT_SYMBOL(cpu_possible_map); | 133 | EXPORT_SYMBOL(cpu_possible_map); |
92 | 134 | ||
135 | cpumask_t cpu_core_map[NR_CPUS] __cacheline_aligned; | ||
136 | cpumask_t cpu_sibling_map[NR_CPUS] __cacheline_aligned; | ||
137 | int smp_num_siblings = 1; | ||
138 | int smp_num_cpucores = 1; | ||
139 | |||
93 | /* which logical CPU number maps to which CPU (physical APIC ID) */ | 140 | /* which logical CPU number maps to which CPU (physical APIC ID) */ |
94 | volatile int ia64_cpu_to_sapicid[NR_CPUS]; | 141 | volatile int ia64_cpu_to_sapicid[NR_CPUS]; |
95 | EXPORT_SYMBOL(ia64_cpu_to_sapicid); | 142 | EXPORT_SYMBOL(ia64_cpu_to_sapicid); |
@@ -124,7 +171,8 @@ sync_master (void *arg) | |||
124 | local_irq_save(flags); | 171 | local_irq_save(flags); |
125 | { | 172 | { |
126 | for (i = 0; i < NUM_ROUNDS*NUM_ITERS; ++i) { | 173 | for (i = 0; i < NUM_ROUNDS*NUM_ITERS; ++i) { |
127 | while (!go[MASTER]); | 174 | while (!go[MASTER]) |
175 | cpu_relax(); | ||
128 | go[MASTER] = 0; | 176 | go[MASTER] = 0; |
129 | go[SLAVE] = ia64_get_itc(); | 177 | go[SLAVE] = ia64_get_itc(); |
130 | } | 178 | } |
@@ -147,7 +195,8 @@ get_delta (long *rt, long *master) | |||
147 | for (i = 0; i < NUM_ITERS; ++i) { | 195 | for (i = 0; i < NUM_ITERS; ++i) { |
148 | t0 = ia64_get_itc(); | 196 | t0 = ia64_get_itc(); |
149 | go[MASTER] = 1; | 197 | go[MASTER] = 1; |
150 | while (!(tm = go[SLAVE])); | 198 | while (!(tm = go[SLAVE])) |
199 | cpu_relax(); | ||
151 | go[SLAVE] = 0; | 200 | go[SLAVE] = 0; |
152 | t1 = ia64_get_itc(); | 201 | t1 = ia64_get_itc(); |
153 | 202 | ||
@@ -226,7 +275,8 @@ ia64_sync_itc (unsigned int master) | |||
226 | return; | 275 | return; |
227 | } | 276 | } |
228 | 277 | ||
229 | while (go[MASTER]); /* wait for master to be ready */ | 278 | while (go[MASTER]) |
279 | cpu_relax(); /* wait for master to be ready */ | ||
230 | 280 | ||
231 | spin_lock_irqsave(&itc_sync_lock, flags); | 281 | spin_lock_irqsave(&itc_sync_lock, flags); |
232 | { | 282 | { |
@@ -345,7 +395,6 @@ start_secondary (void *unused) | |||
345 | { | 395 | { |
346 | /* Early console may use I/O ports */ | 396 | /* Early console may use I/O ports */ |
347 | ia64_set_kr(IA64_KR_IO_BASE, __pa(ia64_iobase)); | 397 | ia64_set_kr(IA64_KR_IO_BASE, __pa(ia64_iobase)); |
348 | |||
349 | Dprintk("start_secondary: starting CPU 0x%x\n", hard_smp_processor_id()); | 398 | Dprintk("start_secondary: starting CPU 0x%x\n", hard_smp_processor_id()); |
350 | efi_map_pal_code(); | 399 | efi_map_pal_code(); |
351 | cpu_init(); | 400 | cpu_init(); |
@@ -384,6 +433,13 @@ do_boot_cpu (int sapicid, int cpu) | |||
384 | .done = COMPLETION_INITIALIZER(c_idle.done), | 433 | .done = COMPLETION_INITIALIZER(c_idle.done), |
385 | }; | 434 | }; |
386 | DECLARE_WORK(work, do_fork_idle, &c_idle); | 435 | DECLARE_WORK(work, do_fork_idle, &c_idle); |
436 | |||
437 | c_idle.idle = get_idle_for_cpu(cpu); | ||
438 | if (c_idle.idle) { | ||
439 | init_idle(c_idle.idle, cpu); | ||
440 | goto do_rest; | ||
441 | } | ||
442 | |||
387 | /* | 443 | /* |
388 | * We can't use kernel_thread since we must avoid to reschedule the child. | 444 | * We can't use kernel_thread since we must avoid to reschedule the child. |
389 | */ | 445 | */ |
@@ -396,10 +452,15 @@ do_boot_cpu (int sapicid, int cpu) | |||
396 | 452 | ||
397 | if (IS_ERR(c_idle.idle)) | 453 | if (IS_ERR(c_idle.idle)) |
398 | panic("failed fork for CPU %d", cpu); | 454 | panic("failed fork for CPU %d", cpu); |
455 | |||
456 | set_idle_for_cpu(cpu, c_idle.idle); | ||
457 | |||
458 | do_rest: | ||
399 | task_for_booting_cpu = c_idle.idle; | 459 | task_for_booting_cpu = c_idle.idle; |
400 | 460 | ||
401 | Dprintk("Sending wakeup vector %lu to AP 0x%x/0x%x.\n", ap_wakeup_vector, cpu, sapicid); | 461 | Dprintk("Sending wakeup vector %lu to AP 0x%x/0x%x.\n", ap_wakeup_vector, cpu, sapicid); |
402 | 462 | ||
463 | set_brendez_area(cpu); | ||
403 | platform_send_ipi(cpu, ap_wakeup_vector, IA64_IPI_DM_INT, 0); | 464 | platform_send_ipi(cpu, ap_wakeup_vector, IA64_IPI_DM_INT, 0); |
404 | 465 | ||
405 | /* | 466 | /* |
@@ -552,19 +613,70 @@ void __devinit smp_prepare_boot_cpu(void) | |||
552 | cpu_set(smp_processor_id(), cpu_callin_map); | 613 | cpu_set(smp_processor_id(), cpu_callin_map); |
553 | } | 614 | } |
554 | 615 | ||
616 | /* | ||
617 | * mt_info[] is a temporary store for all info returned by | ||
618 | * PAL_LOGICAL_TO_PHYSICAL, to be copied into cpuinfo_ia64 when the | ||
619 | * specific cpu comes. | ||
620 | */ | ||
621 | static struct { | ||
622 | __u32 socket_id; | ||
623 | __u16 core_id; | ||
624 | __u16 thread_id; | ||
625 | __u16 proc_fixed_addr; | ||
626 | __u8 valid; | ||
627 | }mt_info[NR_CPUS] __devinit; | ||
628 | |||
555 | #ifdef CONFIG_HOTPLUG_CPU | 629 | #ifdef CONFIG_HOTPLUG_CPU |
556 | extern void fixup_irqs(void); | 630 | static inline void |
557 | /* must be called with cpucontrol mutex held */ | 631 | remove_from_mtinfo(int cpu) |
558 | static int __devinit cpu_enable(unsigned int cpu) | ||
559 | { | 632 | { |
560 | per_cpu(cpu_state,cpu) = CPU_UP_PREPARE; | 633 | int i; |
561 | wmb(); | ||
562 | 634 | ||
563 | while (!cpu_online(cpu)) | 635 | for_each_cpu(i) |
564 | cpu_relax(); | 636 | if (mt_info[i].valid && mt_info[i].socket_id == |
565 | return 0; | 637 | cpu_data(cpu)->socket_id) |
638 | mt_info[i].valid = 0; | ||
639 | } | ||
640 | |||
641 | static inline void | ||
642 | clear_cpu_sibling_map(int cpu) | ||
643 | { | ||
644 | int i; | ||
645 | |||
646 | for_each_cpu_mask(i, cpu_sibling_map[cpu]) | ||
647 | cpu_clear(cpu, cpu_sibling_map[i]); | ||
648 | for_each_cpu_mask(i, cpu_core_map[cpu]) | ||
649 | cpu_clear(cpu, cpu_core_map[i]); | ||
650 | |||
651 | cpu_sibling_map[cpu] = cpu_core_map[cpu] = CPU_MASK_NONE; | ||
652 | } | ||
653 | |||
654 | static void | ||
655 | remove_siblinginfo(int cpu) | ||
656 | { | ||
657 | int last = 0; | ||
658 | |||
659 | if (cpu_data(cpu)->threads_per_core == 1 && | ||
660 | cpu_data(cpu)->cores_per_socket == 1) { | ||
661 | cpu_clear(cpu, cpu_core_map[cpu]); | ||
662 | cpu_clear(cpu, cpu_sibling_map[cpu]); | ||
663 | return; | ||
664 | } | ||
665 | |||
666 | last = (cpus_weight(cpu_core_map[cpu]) == 1 ? 1 : 0); | ||
667 | |||
668 | /* remove it from all sibling map's */ | ||
669 | clear_cpu_sibling_map(cpu); | ||
670 | |||
671 | /* if this cpu is the last in the core group, remove all its info | ||
672 | * from mt_info structure | ||
673 | */ | ||
674 | if (last) | ||
675 | remove_from_mtinfo(cpu); | ||
566 | } | 676 | } |
567 | 677 | ||
678 | extern void fixup_irqs(void); | ||
679 | /* must be called with cpucontrol mutex held */ | ||
568 | int __cpu_disable(void) | 680 | int __cpu_disable(void) |
569 | { | 681 | { |
570 | int cpu = smp_processor_id(); | 682 | int cpu = smp_processor_id(); |
@@ -575,9 +687,10 @@ int __cpu_disable(void) | |||
575 | if (cpu == 0) | 687 | if (cpu == 0) |
576 | return -EBUSY; | 688 | return -EBUSY; |
577 | 689 | ||
690 | remove_siblinginfo(cpu); | ||
578 | fixup_irqs(); | 691 | fixup_irqs(); |
579 | local_flush_tlb_all(); | 692 | local_flush_tlb_all(); |
580 | printk ("Disabled cpu %u\n", smp_processor_id()); | 693 | cpu_clear(cpu, cpu_callin_map); |
581 | return 0; | 694 | return 0; |
582 | } | 695 | } |
583 | 696 | ||
@@ -589,12 +702,7 @@ void __cpu_die(unsigned int cpu) | |||
589 | /* They ack this in play_dead by setting CPU_DEAD */ | 702 | /* They ack this in play_dead by setting CPU_DEAD */ |
590 | if (per_cpu(cpu_state, cpu) == CPU_DEAD) | 703 | if (per_cpu(cpu_state, cpu) == CPU_DEAD) |
591 | { | 704 | { |
592 | /* | 705 | printk ("CPU %d is now offline\n", cpu); |
593 | * TBD: Enable this when physical removal | ||
594 | * or when we put the processor is put in | ||
595 | * SAL_BOOT_RENDEZ mode | ||
596 | * cpu_clear(cpu, cpu_callin_map); | ||
597 | */ | ||
598 | return; | 706 | return; |
599 | } | 707 | } |
600 | msleep(100); | 708 | msleep(100); |
@@ -602,11 +710,6 @@ void __cpu_die(unsigned int cpu) | |||
602 | printk(KERN_ERR "CPU %u didn't die...\n", cpu); | 710 | printk(KERN_ERR "CPU %u didn't die...\n", cpu); |
603 | } | 711 | } |
604 | #else /* !CONFIG_HOTPLUG_CPU */ | 712 | #else /* !CONFIG_HOTPLUG_CPU */ |
605 | static int __devinit cpu_enable(unsigned int cpu) | ||
606 | { | ||
607 | return 0; | ||
608 | } | ||
609 | |||
610 | int __cpu_disable(void) | 713 | int __cpu_disable(void) |
611 | { | 714 | { |
612 | return -ENOSYS; | 715 | return -ENOSYS; |
@@ -637,6 +740,23 @@ smp_cpus_done (unsigned int dummy) | |||
637 | (int)num_online_cpus(), bogosum/(500000/HZ), (bogosum/(5000/HZ))%100); | 740 | (int)num_online_cpus(), bogosum/(500000/HZ), (bogosum/(5000/HZ))%100); |
638 | } | 741 | } |
639 | 742 | ||
743 | static inline void __devinit | ||
744 | set_cpu_sibling_map(int cpu) | ||
745 | { | ||
746 | int i; | ||
747 | |||
748 | for_each_online_cpu(i) { | ||
749 | if ((cpu_data(cpu)->socket_id == cpu_data(i)->socket_id)) { | ||
750 | cpu_set(i, cpu_core_map[cpu]); | ||
751 | cpu_set(cpu, cpu_core_map[i]); | ||
752 | if (cpu_data(cpu)->core_id == cpu_data(i)->core_id) { | ||
753 | cpu_set(i, cpu_sibling_map[cpu]); | ||
754 | cpu_set(cpu, cpu_sibling_map[i]); | ||
755 | } | ||
756 | } | ||
757 | } | ||
758 | } | ||
759 | |||
640 | int __devinit | 760 | int __devinit |
641 | __cpu_up (unsigned int cpu) | 761 | __cpu_up (unsigned int cpu) |
642 | { | 762 | { |
@@ -648,21 +768,26 @@ __cpu_up (unsigned int cpu) | |||
648 | return -EINVAL; | 768 | return -EINVAL; |
649 | 769 | ||
650 | /* | 770 | /* |
651 | * Already booted.. just enable and get outa idle lool | 771 | * Already booted cpu? not valid anymore since we dont |
772 | * do idle loop tightspin anymore. | ||
652 | */ | 773 | */ |
653 | if (cpu_isset(cpu, cpu_callin_map)) | 774 | if (cpu_isset(cpu, cpu_callin_map)) |
654 | { | 775 | return -EINVAL; |
655 | cpu_enable(cpu); | 776 | |
656 | local_irq_enable(); | ||
657 | while (!cpu_isset(cpu, cpu_online_map)) | ||
658 | mb(); | ||
659 | return 0; | ||
660 | } | ||
661 | /* Processor goes to start_secondary(), sets online flag */ | 777 | /* Processor goes to start_secondary(), sets online flag */ |
662 | ret = do_boot_cpu(sapicid, cpu); | 778 | ret = do_boot_cpu(sapicid, cpu); |
663 | if (ret < 0) | 779 | if (ret < 0) |
664 | return ret; | 780 | return ret; |
665 | 781 | ||
782 | if (cpu_data(cpu)->threads_per_core == 1 && | ||
783 | cpu_data(cpu)->cores_per_socket == 1) { | ||
784 | cpu_set(cpu, cpu_sibling_map[cpu]); | ||
785 | cpu_set(cpu, cpu_core_map[cpu]); | ||
786 | return 0; | ||
787 | } | ||
788 | |||
789 | set_cpu_sibling_map(cpu); | ||
790 | |||
666 | return 0; | 791 | return 0; |
667 | } | 792 | } |
668 | 793 | ||
@@ -690,3 +815,106 @@ init_smp_config(void) | |||
690 | ia64_sal_strerror(sal_ret)); | 815 | ia64_sal_strerror(sal_ret)); |
691 | } | 816 | } |
692 | 817 | ||
818 | static inline int __devinit | ||
819 | check_for_mtinfo_index(void) | ||
820 | { | ||
821 | int i; | ||
822 | |||
823 | for_each_cpu(i) | ||
824 | if (!mt_info[i].valid) | ||
825 | return i; | ||
826 | |||
827 | return -1; | ||
828 | } | ||
829 | |||
830 | /* | ||
831 | * Search the mt_info to find out if this socket's cid/tid information is | ||
832 | * cached or not. If the socket exists, fill in the core_id and thread_id | ||
833 | * in cpuinfo | ||
834 | */ | ||
835 | static int __devinit | ||
836 | check_for_new_socket(__u16 logical_address, struct cpuinfo_ia64 *c) | ||
837 | { | ||
838 | int i; | ||
839 | __u32 sid = c->socket_id; | ||
840 | |||
841 | for_each_cpu(i) { | ||
842 | if (mt_info[i].valid && mt_info[i].proc_fixed_addr == logical_address | ||
843 | && mt_info[i].socket_id == sid) { | ||
844 | c->core_id = mt_info[i].core_id; | ||
845 | c->thread_id = mt_info[i].thread_id; | ||
846 | return 1; /* not a new socket */ | ||
847 | } | ||
848 | } | ||
849 | return 0; | ||
850 | } | ||
851 | |||
852 | /* | ||
853 | * identify_siblings(cpu) gets called from identify_cpu. This populates the | ||
854 | * information related to logical execution units in per_cpu_data structure. | ||
855 | */ | ||
856 | void __devinit | ||
857 | identify_siblings(struct cpuinfo_ia64 *c) | ||
858 | { | ||
859 | s64 status; | ||
860 | u16 pltid; | ||
861 | u64 proc_fixed_addr; | ||
862 | int count, i; | ||
863 | pal_logical_to_physical_t info; | ||
864 | |||
865 | if (smp_num_cpucores == 1 && smp_num_siblings == 1) | ||
866 | return; | ||
867 | |||
868 | if ((status = ia64_pal_logical_to_phys(0, &info)) != PAL_STATUS_SUCCESS) { | ||
869 | printk(KERN_ERR "ia64_pal_logical_to_phys failed with %ld\n", | ||
870 | status); | ||
871 | return; | ||
872 | } | ||
873 | if ((status = ia64_sal_physical_id_info(&pltid)) != PAL_STATUS_SUCCESS) { | ||
874 | printk(KERN_ERR "ia64_sal_pltid failed with %ld\n", status); | ||
875 | return; | ||
876 | } | ||
877 | if ((status = ia64_pal_fixed_addr(&proc_fixed_addr)) != PAL_STATUS_SUCCESS) { | ||
878 | printk(KERN_ERR "ia64_pal_fixed_addr failed with %ld\n", status); | ||
879 | return; | ||
880 | } | ||
881 | |||
882 | c->socket_id = (pltid << 8) | info.overview_ppid; | ||
883 | c->cores_per_socket = info.overview_cpp; | ||
884 | c->threads_per_core = info.overview_tpc; | ||
885 | count = c->num_log = info.overview_num_log; | ||
886 | |||
887 | /* If the thread and core id information is already cached, then | ||
888 | * we will simply update cpu_info and return. Otherwise, we will | ||
889 | * do the PAL calls and cache core and thread id's of all the siblings. | ||
890 | */ | ||
891 | if (check_for_new_socket(proc_fixed_addr, c)) | ||
892 | return; | ||
893 | |||
894 | for (i = 0; i < count; i++) { | ||
895 | int index; | ||
896 | |||
897 | if (i && (status = ia64_pal_logical_to_phys(i, &info)) | ||
898 | != PAL_STATUS_SUCCESS) { | ||
899 | printk(KERN_ERR "ia64_pal_logical_to_phys failed" | ||
900 | " with %ld\n", status); | ||
901 | return; | ||
902 | } | ||
903 | if (info.log2_la == proc_fixed_addr) { | ||
904 | c->core_id = info.log1_cid; | ||
905 | c->thread_id = info.log1_tid; | ||
906 | } | ||
907 | |||
908 | index = check_for_mtinfo_index(); | ||
909 | /* We will not do the mt_info caching optimization in this case. | ||
910 | */ | ||
911 | if (index < 0) | ||
912 | continue; | ||
913 | |||
914 | mt_info[index].valid = 1; | ||
915 | mt_info[index].socket_id = c->socket_id; | ||
916 | mt_info[index].core_id = info.log1_cid; | ||
917 | mt_info[index].thread_id = info.log1_tid; | ||
918 | mt_info[index].proc_fixed_addr = info.log2_la; | ||
919 | } | ||
920 | } | ||