Commit 1426d5a3 authored by Michael Ellerman's avatar Michael Ellerman Committed by Benjamin Herrenschmidt

powerpc: Dynamically allocate pacas

On 64-bit kernels we currently have a 512 byte struct paca_struct for
each cpu (usually just called "the paca"). Currently they are statically
allocated, which means a kernel built for a large number of cpus will
waste a lot of space if it's booted on a machine with few cpus.

We can avoid that by only allocating the number of pacas we need at
boot. However this is complicated by the fact that we need to access
the paca before we know how many cpus there are in the system.

The solution is to dynamically allocate enough space for NR_CPUS pacas,
but then later in boot when we know how many cpus we have, we free any
unused pacas.
Signed-off-by: default avatarMichael Ellerman <michael@ellerman.id.au>
Signed-off-by: default avatarBenjamin Herrenschmidt <benh@kernel.crashing.org>
parent 59603b9a
...@@ -14,6 +14,9 @@ ...@@ -14,6 +14,9 @@
#define _ASM_POWERPC_PACA_H #define _ASM_POWERPC_PACA_H
#ifdef __KERNEL__ #ifdef __KERNEL__
#ifdef CONFIG_PPC64
#include <linux/init.h>
#include <asm/types.h> #include <asm/types.h>
#include <asm/lppaca.h> #include <asm/lppaca.h>
#include <asm/mmu.h> #include <asm/mmu.h>
...@@ -145,8 +148,19 @@ struct paca_struct { ...@@ -145,8 +148,19 @@ struct paca_struct {
#endif #endif
}; };
extern struct paca_struct paca[]; extern struct paca_struct *paca;
extern void initialise_pacas(void); extern __initdata struct paca_struct boot_paca;
extern void initialise_paca(struct paca_struct *new_paca, int cpu);
extern void allocate_pacas(void);
extern void free_unused_pacas(void);
#else /* CONFIG_PPC64 */
static inline void allocate_pacas(void) { };
static inline void free_unused_pacas(void) { };
#endif /* CONFIG_PPC64 */
#endif /* __KERNEL__ */ #endif /* __KERNEL__ */
#endif /* _ASM_POWERPC_PACA_H */ #endif /* _ASM_POWERPC_PACA_H */
...@@ -219,7 +219,8 @@ generic_secondary_common_init: ...@@ -219,7 +219,8 @@ generic_secondary_common_init:
* physical cpu id in r24, we need to search the pacas to find * physical cpu id in r24, we need to search the pacas to find
* which logical id maps to our physical one. * which logical id maps to our physical one.
*/ */
LOAD_REG_ADDR(r13, paca) /* Get base vaddr of paca array */ LOAD_REG_ADDR(r13, paca) /* Load paca pointer */
ld r13,0(r13) /* Get base vaddr of paca array */
li r5,0 /* logical cpu id */ li r5,0 /* logical cpu id */
1: lhz r6,PACAHWCPUID(r13) /* Load HW procid from paca */ 1: lhz r6,PACAHWCPUID(r13) /* Load HW procid from paca */
cmpw r6,r24 /* Compare to our id */ cmpw r6,r24 /* Compare to our id */
...@@ -536,7 +537,8 @@ _GLOBAL(pmac_secondary_start) ...@@ -536,7 +537,8 @@ _GLOBAL(pmac_secondary_start)
mtmsrd r3 /* RI on */ mtmsrd r3 /* RI on */
/* Set up a paca value for this processor. */ /* Set up a paca value for this processor. */
LOAD_REG_ADDR(r4,paca) /* Get base vaddr of paca array */ LOAD_REG_ADDR(r4,paca) /* Load paca pointer */
ld r4,0(r4) /* Get base vaddr of paca array */
mulli r13,r24,PACA_SIZE /* Calculate vaddr of right paca */ mulli r13,r24,PACA_SIZE /* Calculate vaddr of right paca */
add r13,r13,r4 /* for this processor. */ add r13,r13,r4 /* for this processor. */
mtspr SPRN_SPRG_PACA,r13 /* Save vaddr of paca in an SPRG*/ mtspr SPRN_SPRG_PACA,r13 /* Save vaddr of paca in an SPRG*/
......
...@@ -9,11 +9,15 @@ ...@@ -9,11 +9,15 @@
#include <linux/threads.h> #include <linux/threads.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/lmb.h>
#include <asm/firmware.h>
#include <asm/lppaca.h> #include <asm/lppaca.h>
#include <asm/paca.h> #include <asm/paca.h>
#include <asm/sections.h> #include <asm/sections.h>
#include <asm/pgtable.h> #include <asm/pgtable.h>
#include <asm/iseries/lpar_map.h>
#include <asm/iseries/hv_types.h>
/* This symbol is provided by the linker - let it fill in the paca /* This symbol is provided by the linker - let it fill in the paca
* field correctly */ * field correctly */
...@@ -70,22 +74,18 @@ struct slb_shadow slb_shadow[] __cacheline_aligned = { ...@@ -70,22 +74,18 @@ struct slb_shadow slb_shadow[] __cacheline_aligned = {
* processors. The processor VPD array needs one entry per physical * processors. The processor VPD array needs one entry per physical
* processor (not thread). * processor (not thread).
*/ */
struct paca_struct paca[NR_CPUS]; struct paca_struct *paca;
EXPORT_SYMBOL(paca); EXPORT_SYMBOL(paca);
void __init initialise_pacas(void) struct paca_struct boot_paca;
{
int cpu;
void __init initialise_paca(struct paca_struct *new_paca, int cpu)
{
/* The TOC register (GPR2) points 32kB into the TOC, so that 64kB /* The TOC register (GPR2) points 32kB into the TOC, so that 64kB
* of the TOC can be addressed using a single machine instruction. * of the TOC can be addressed using a single machine instruction.
*/ */
unsigned long kernel_toc = (unsigned long)(&__toc_start) + 0x8000UL; unsigned long kernel_toc = (unsigned long)(&__toc_start) + 0x8000UL;
/* Can't use for_each_*_cpu, as they aren't functional yet */
for (cpu = 0; cpu < NR_CPUS; cpu++) {
struct paca_struct *new_paca = &paca[cpu];
#ifdef CONFIG_PPC_BOOK3S #ifdef CONFIG_PPC_BOOK3S
new_paca->lppaca_ptr = &lppaca[cpu]; new_paca->lppaca_ptr = &lppaca[cpu];
#else #else
...@@ -101,6 +101,55 @@ void __init initialise_pacas(void) ...@@ -101,6 +101,55 @@ void __init initialise_pacas(void)
#ifdef CONFIG_PPC_STD_MMU_64 #ifdef CONFIG_PPC_STD_MMU_64
new_paca->slb_shadow_ptr = &slb_shadow[cpu]; new_paca->slb_shadow_ptr = &slb_shadow[cpu];
#endif /* CONFIG_PPC_STD_MMU_64 */ #endif /* CONFIG_PPC_STD_MMU_64 */
}
static int __initdata paca_size;
void __init allocate_pacas(void)
{
int nr_cpus, cpu, limit;
/*
* We can't take SLB misses on the paca, and we want to access them
* in real mode, so allocate them within the RMA and also within
* the first segment. On iSeries they must be within the area mapped
* by the HV, which is HvPagesToMap * HVPAGESIZE bytes.
*/
limit = min(0x10000000ULL, lmb.rmo_size);
if (firmware_has_feature(FW_FEATURE_ISERIES))
limit = min(limit, HvPagesToMap * HVPAGESIZE);
nr_cpus = NR_CPUS;
/* On iSeries we know we can never have more than 64 cpus */
if (firmware_has_feature(FW_FEATURE_ISERIES))
nr_cpus = min(64, nr_cpus);
paca_size = PAGE_ALIGN(sizeof(struct paca_struct) * nr_cpus);
paca = __va(lmb_alloc_base(paca_size, PAGE_SIZE, limit));
memset(paca, 0, paca_size);
printk(KERN_DEBUG "Allocated %u bytes for %d pacas at %p\n",
paca_size, nr_cpus, paca);
/* Can't use for_each_*_cpu, as they aren't functional yet */
for (cpu = 0; cpu < nr_cpus; cpu++)
initialise_paca(&paca[cpu], cpu);
}
void __init free_unused_pacas(void)
{
int new_size;
new_size = PAGE_ALIGN(sizeof(struct paca_struct) * num_possible_cpus());
if (new_size >= paca_size)
return;
lmb_free(__pa(paca) + new_size, paca_size - new_size);
printk(KERN_DEBUG "Freed %u bytes for unused pacas\n",
paca_size - new_size);
} paca_size = new_size;
} }
...@@ -43,6 +43,7 @@ ...@@ -43,6 +43,7 @@
#include <asm/smp.h> #include <asm/smp.h>
#include <asm/system.h> #include <asm/system.h>
#include <asm/mmu.h> #include <asm/mmu.h>
#include <asm/paca.h>
#include <asm/pgtable.h> #include <asm/pgtable.h>
#include <asm/pci.h> #include <asm/pci.h>
#include <asm/iommu.h> #include <asm/iommu.h>
...@@ -721,6 +722,8 @@ void __init early_init_devtree(void *params) ...@@ -721,6 +722,8 @@ void __init early_init_devtree(void *params)
* FIXME .. and the initrd too? */ * FIXME .. and the initrd too? */
move_device_tree(); move_device_tree();
allocate_pacas();
DBG("Scanning CPUs ...\n"); DBG("Scanning CPUs ...\n");
/* Retreive CPU related informations from the flat tree /* Retreive CPU related informations from the flat tree
......
...@@ -36,6 +36,7 @@ ...@@ -36,6 +36,7 @@
#include <linux/lmb.h> #include <linux/lmb.h>
#include <linux/of_platform.h> #include <linux/of_platform.h>
#include <asm/io.h> #include <asm/io.h>
#include <asm/paca.h>
#include <asm/prom.h> #include <asm/prom.h>
#include <asm/processor.h> #include <asm/processor.h>
#include <asm/vdso_datapage.h> #include <asm/vdso_datapage.h>
...@@ -493,6 +494,8 @@ void __init smp_setup_cpu_maps(void) ...@@ -493,6 +494,8 @@ void __init smp_setup_cpu_maps(void)
* here will have to be reworked * here will have to be reworked
*/ */
cpu_init_thread_core_maps(nthreads); cpu_init_thread_core_maps(nthreads);
free_unused_pacas();
} }
#endif /* CONFIG_SMP */ #endif /* CONFIG_SMP */
......
...@@ -144,9 +144,9 @@ early_param("smt-enabled", early_smt_enabled); ...@@ -144,9 +144,9 @@ early_param("smt-enabled", early_smt_enabled);
#endif /* CONFIG_SMP */ #endif /* CONFIG_SMP */
/* Put the paca pointer into r13 and SPRG_PACA */ /* Put the paca pointer into r13 and SPRG_PACA */
void __init setup_paca(int cpu) static void __init setup_paca(struct paca_struct *new_paca)
{ {
local_paca = &paca[cpu]; local_paca = new_paca;
mtspr(SPRN_SPRG_PACA, local_paca); mtspr(SPRN_SPRG_PACA, local_paca);
#ifdef CONFIG_PPC_BOOK3E #ifdef CONFIG_PPC_BOOK3E
mtspr(SPRN_SPRG_TLB_EXFRAME, local_paca->extlb); mtspr(SPRN_SPRG_TLB_EXFRAME, local_paca->extlb);
...@@ -176,14 +176,12 @@ void __init early_setup(unsigned long dt_ptr) ...@@ -176,14 +176,12 @@ void __init early_setup(unsigned long dt_ptr)
{ {
/* -------- printk is _NOT_ safe to use here ! ------- */ /* -------- printk is _NOT_ safe to use here ! ------- */
/* Fill in any unititialised pacas */
initialise_pacas();
/* Identify CPU type */ /* Identify CPU type */
identify_cpu(0, mfspr(SPRN_PVR)); identify_cpu(0, mfspr(SPRN_PVR));
/* Assume we're on cpu 0 for now. Don't write to the paca yet! */ /* Assume we're on cpu 0 for now. Don't write to the paca yet! */
setup_paca(0); initialise_paca(&boot_paca, 0);
setup_paca(&boot_paca);
/* Initialize lockdep early or else spinlocks will blow */ /* Initialize lockdep early or else spinlocks will blow */
lockdep_init(); lockdep_init();
...@@ -203,7 +201,7 @@ void __init early_setup(unsigned long dt_ptr) ...@@ -203,7 +201,7 @@ void __init early_setup(unsigned long dt_ptr)
early_init_devtree(__va(dt_ptr)); early_init_devtree(__va(dt_ptr));
/* Now we know the logical id of our boot cpu, setup the paca. */ /* Now we know the logical id of our boot cpu, setup the paca. */
setup_paca(boot_cpuid); setup_paca(&paca[boot_cpuid]);
/* Fix up paca fields required for the boot cpu */ /* Fix up paca fields required for the boot cpu */
get_paca()->cpu_start = 1; get_paca()->cpu_start = 1;
......
...@@ -43,17 +43,14 @@ system_reset_iSeries: ...@@ -43,17 +43,14 @@ system_reset_iSeries:
LOAD_REG_ADDR(r23, alpaca) LOAD_REG_ADDR(r23, alpaca)
li r0,ALPACA_SIZE li r0,ALPACA_SIZE
sub r23,r13,r23 sub r23,r13,r23
divdu r23,r23,r0 /* r23 has cpu number */ divdu r24,r23,r0 /* r24 has cpu number */
LOAD_REG_ADDR(r13, paca)
mulli r0,r23,PACA_SIZE
add r13,r13,r0
mtspr SPRN_SPRG_PACA,r13 /* Save it away for the future */
mfmsr r24
ori r24,r24,MSR_RI
mtmsrd r24 /* RI on */
mr r24,r23
cmpwi 0,r24,0 /* Are we processor 0? */ cmpwi 0,r24,0 /* Are we processor 0? */
bne 1f bne 1f
LOAD_REG_ADDR(r13, boot_paca)
mtspr SPRN_SPRG_PACA,r13 /* Save it away for the future */
mfmsr r23
ori r23,r23,MSR_RI
mtmsrd r23 /* RI on */
b .__start_initialization_iSeries /* Start up the first processor */ b .__start_initialization_iSeries /* Start up the first processor */
1: mfspr r4,SPRN_CTRLF 1: mfspr r4,SPRN_CTRLF
li r5,CTRL_RUNLATCH /* Turn off the run light */ li r5,CTRL_RUNLATCH /* Turn off the run light */
...@@ -86,6 +83,16 @@ system_reset_iSeries: ...@@ -86,6 +83,16 @@ system_reset_iSeries:
#endif #endif
2: 2:
/* Load our paca now that it's been allocated */
LOAD_REG_ADDR(r13, paca)
ld r13,0(r13)
mulli r0,r24,PACA_SIZE
add r13,r13,r0
mtspr SPRN_SPRG_PACA,r13 /* Save it away for the future */
mfmsr r23
ori r23,r23,MSR_RI
mtmsrd r23 /* RI on */
HMT_LOW HMT_LOW
#ifdef CONFIG_SMP #ifdef CONFIG_SMP
lbz r23,PACAPROCSTART(r13) /* Test if this processor lbz r23,PACAPROCSTART(r13) /* Test if this processor
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment