diff options
Diffstat (limited to 'arch/powerpc/kernel/paca.c')
-rw-r--r-- | arch/powerpc/kernel/paca.c | 70 |
1 files changed, 68 insertions, 2 deletions
diff --git a/arch/powerpc/kernel/paca.c b/arch/powerpc/kernel/paca.c index a4e72159234f..ebf9846f3c3b 100644 --- a/arch/powerpc/kernel/paca.c +++ b/arch/powerpc/kernel/paca.c | |||
@@ -27,6 +27,20 @@ extern unsigned long __toc_start; | |||
27 | #ifdef CONFIG_PPC_BOOK3S | 27 | #ifdef CONFIG_PPC_BOOK3S |
28 | 28 | ||
29 | /* | 29 | /* |
30 | * We only have to have statically allocated lppaca structs on | ||
31 | * legacy iSeries, which supports at most 64 cpus. | ||
32 | */ | ||
33 | #ifdef CONFIG_PPC_ISERIES | ||
34 | #if NR_CPUS < 64 | ||
35 | #define NR_LPPACAS NR_CPUS | ||
36 | #else | ||
37 | #define NR_LPPACAS 64 | ||
38 | #endif | ||
39 | #else /* not iSeries */ | ||
40 | #define NR_LPPACAS 1 | ||
41 | #endif | ||
42 | |||
43 | /* | ||
30 | * The structure which the hypervisor knows about - this structure | 44 | * The structure which the hypervisor knows about - this structure |
31 | * should not cross a page boundary. The vpa_init/register_vpa call | 45 | * should not cross a page boundary. The vpa_init/register_vpa call |
32 | * is now known to fail if the lppaca structure crosses a page | 46 | * is now known to fail if the lppaca structure crosses a page |
@@ -36,7 +50,7 @@ extern unsigned long __toc_start; | |||
36 | * will suffice to ensure that it doesn't cross a page boundary. | 50 | * will suffice to ensure that it doesn't cross a page boundary. |
37 | */ | 51 | */ |
38 | struct lppaca lppaca[] = { | 52 | struct lppaca lppaca[] = { |
39 | [0 ... (NR_CPUS-1)] = { | 53 | [0 ... (NR_LPPACAS-1)] = { |
40 | .desc = 0xd397d781, /* "LpPa" */ | 54 | .desc = 0xd397d781, /* "LpPa" */ |
41 | .size = sizeof(struct lppaca), | 55 | .size = sizeof(struct lppaca), |
42 | .dyn_proc_status = 2, | 56 | .dyn_proc_status = 2, |
@@ -49,6 +63,54 @@ struct lppaca lppaca[] = { | |||
49 | }, | 63 | }, |
50 | }; | 64 | }; |
51 | 65 | ||
66 | static struct lppaca *extra_lppacas; | ||
67 | static long __initdata lppaca_size; | ||
68 | |||
69 | static void allocate_lppacas(int nr_cpus, unsigned long limit) | ||
70 | { | ||
71 | if (nr_cpus <= NR_LPPACAS) | ||
72 | return; | ||
73 | |||
74 | lppaca_size = PAGE_ALIGN(sizeof(struct lppaca) * | ||
75 | (nr_cpus - NR_LPPACAS)); | ||
76 | extra_lppacas = __va(memblock_alloc_base(lppaca_size, | ||
77 | PAGE_SIZE, limit)); | ||
78 | } | ||
79 | |||
80 | static struct lppaca *new_lppaca(int cpu) | ||
81 | { | ||
82 | struct lppaca *lp; | ||
83 | |||
84 | if (cpu < NR_LPPACAS) | ||
85 | return &lppaca[cpu]; | ||
86 | |||
87 | lp = extra_lppacas + (cpu - NR_LPPACAS); | ||
88 | *lp = lppaca[0]; | ||
89 | |||
90 | return lp; | ||
91 | } | ||
92 | |||
93 | static void free_lppacas(void) | ||
94 | { | ||
95 | long new_size = 0, nr; | ||
96 | |||
97 | if (!lppaca_size) | ||
98 | return; | ||
99 | nr = num_possible_cpus() - NR_LPPACAS; | ||
100 | if (nr > 0) | ||
101 | new_size = PAGE_ALIGN(nr * sizeof(struct lppaca)); | ||
102 | if (new_size >= lppaca_size) | ||
103 | return; | ||
104 | |||
105 | memblock_free(__pa(extra_lppacas) + new_size, lppaca_size - new_size); | ||
106 | lppaca_size = new_size; | ||
107 | } | ||
108 | |||
109 | #else | ||
110 | |||
111 | static inline void allocate_lppacas(int nr_cpus, unsigned long limit) { } | ||
112 | static inline void free_lppacas(void) { } | ||
113 | |||
52 | #endif /* CONFIG_PPC_BOOK3S */ | 114 | #endif /* CONFIG_PPC_BOOK3S */ |
53 | 115 | ||
54 | #ifdef CONFIG_PPC_STD_MMU_64 | 116 | #ifdef CONFIG_PPC_STD_MMU_64 |
@@ -88,7 +150,7 @@ void __init initialise_paca(struct paca_struct *new_paca, int cpu) | |||
88 | unsigned long kernel_toc = (unsigned long)(&__toc_start) + 0x8000UL; | 150 | unsigned long kernel_toc = (unsigned long)(&__toc_start) + 0x8000UL; |
89 | 151 | ||
90 | #ifdef CONFIG_PPC_BOOK3S | 152 | #ifdef CONFIG_PPC_BOOK3S |
91 | new_paca->lppaca_ptr = &lppaca[cpu]; | 153 | new_paca->lppaca_ptr = new_lppaca(cpu); |
92 | #else | 154 | #else |
93 | new_paca->kernel_pgd = swapper_pg_dir; | 155 | new_paca->kernel_pgd = swapper_pg_dir; |
94 | #endif | 156 | #endif |
@@ -144,6 +206,8 @@ void __init allocate_pacas(void) | |||
144 | printk(KERN_DEBUG "Allocated %u bytes for %d pacas at %p\n", | 206 | printk(KERN_DEBUG "Allocated %u bytes for %d pacas at %p\n", |
145 | paca_size, nr_cpus, paca); | 207 | paca_size, nr_cpus, paca); |
146 | 208 | ||
209 | allocate_lppacas(nr_cpus, limit); | ||
210 | |||
147 | /* Can't use for_each_*_cpu, as they aren't functional yet */ | 211 | /* Can't use for_each_*_cpu, as they aren't functional yet */ |
148 | for (cpu = 0; cpu < nr_cpus; cpu++) | 212 | for (cpu = 0; cpu < nr_cpus; cpu++) |
149 | initialise_paca(&paca[cpu], cpu); | 213 | initialise_paca(&paca[cpu], cpu); |
@@ -164,4 +228,6 @@ void __init free_unused_pacas(void) | |||
164 | paca_size - new_size); | 228 | paca_size - new_size); |
165 | 229 | ||
166 | paca_size = new_size; | 230 | paca_size = new_size; |
231 | |||
232 | free_lppacas(); | ||
167 | } | 233 | } |