In message <39fb8f1aeab9940b86c940b9a5f8e6bd41ec316c.1263368253.git.mich...@ell erman.id.au> you wrote: > 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. > > Lightly booted on Legacy iSeries & pSeries LPAR.
This is broken on 32bit CPUs. Dies at compile time with mpc86xx_defconfig. No paca on 32bit :-( /home/mikey/src/linux-2.6-ozlabs/arch/powerpc/kernel/prom.c: In function ‘early_init_devtree’: /home/mikey/src/linux-2.6-ozlabs/arch/powerpc/kernel/prom.c:1227: error: implicit declaration of function ‘allocate_pacas’ make[2]: *** [arch/powerpc/kernel/prom.o] Error 1 make[2]: *** Waiting for unfinished jobs.... /home/mikey/src/linux-2.6-ozlabs/arch/powerpc/kernel/setup-common.c: In function ‘smp_setup_cpu_maps’: /home/mikey/src/linux-2.6-ozlabs/arch/powerpc/kernel/setup-common.c:497: error: implicit declaration of function ‘free_unused_pacas’ make[2]: *** [arch/powerpc/kernel/setup-common.o] Error 1 make[1]: *** [arch/powerpc/kernel] Error 2 make[1]: *** Waiting for unfinished jobs.... Mikey > > Signed-off-by: Michael Ellerman <mich...@ellerman.id.au> > --- > arch/powerpc/include/asm/paca.h | 8 ++- > arch/powerpc/kernel/head_64.S | 6 +- > arch/powerpc/kernel/paca.c | 93 +++++++++++++++++++++----- -- > arch/powerpc/kernel/prom.c | 2 + > arch/powerpc/kernel/setup-common.c | 2 + > arch/powerpc/kernel/setup_64.c | 12 ++-- > arch/powerpc/platforms/iseries/exception.S | 25 +++++--- > 7 files changed, 106 insertions(+), 42 deletions(-) > > diff --git a/arch/powerpc/include/asm/paca.h b/arch/powerpc/include/asm/paca. h > index 5e9b4ef..41e0f17 100644 > --- a/arch/powerpc/include/asm/paca.h > +++ b/arch/powerpc/include/asm/paca.h > @@ -14,6 +14,7 @@ > #define _ASM_POWERPC_PACA_H > #ifdef __KERNEL__ > > +#include <linux/init.h> > #include <asm/types.h> > #include <asm/lppaca.h> > #include <asm/mmu.h> > @@ -140,8 +141,11 @@ struct paca_struct { > #endif > }; > > -extern struct paca_struct paca[]; > -extern void initialise_pacas(void); > +extern struct paca_struct *paca; > +extern __initdata struct paca_struct boot_paca; > +extern void allocate_pacas(void); > +extern void initialise_paca(struct paca_struct *new_paca, int cpu); > +extern void free_unused_pacas(void); > > #endif /* __KERNEL__ */ > #endif /* _ASM_POWERPC_PACA_H */ > diff --git a/arch/powerpc/kernel/head_64.S b/arch/powerpc/kernel/head_64.S > index 9258074..b24b700 100644 > --- a/arch/powerpc/kernel/head_64.S > +++ b/arch/powerpc/kernel/head_64.S > @@ -219,7 +219,8 @@ generic_secondary_common_init: > * physical cpu id in r24, we need to search the pacas to find > * 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 */ > 1: lhz r6,PACAHWCPUID(r13) /* Load HW procid from paca */ > cmpw r6,r24 /* Compare to our id */ > @@ -536,7 +537,8 @@ _GLOBAL(pmac_secondary_start) > mtmsrd r3 /* RI on */ > > /* 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 */ > add r13,r13,r4 /* for this processor. */ > mtspr SPRN_SPRG_PACA,r13 /* Save vaddr of paca in an SPRG*/ > diff --git a/arch/powerpc/kernel/paca.c b/arch/powerpc/kernel/paca.c > index d16b1ea..0c40c6f 100644 > --- a/arch/powerpc/kernel/paca.c > +++ b/arch/powerpc/kernel/paca.c > @@ -9,11 +9,15 @@ > > #include <linux/threads.h> > #include <linux/module.h> > +#include <linux/lmb.h> > > +#include <asm/firmware.h> > #include <asm/lppaca.h> > #include <asm/paca.h> > #include <asm/sections.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 > * field correctly */ > @@ -70,37 +74,82 @@ struct slb_shadow slb_shadow[] __cacheline_aligned = { > * processors. The processor VPD array needs one entry per physical > * processor (not thread). > */ > -struct paca_struct paca[NR_CPUS]; > +struct paca_struct *paca; > EXPORT_SYMBOL(paca); > > -void __init initialise_pacas(void) > -{ > - int cpu; > +struct paca_struct boot_paca; > > - /* The TOC register (GPR2) points 32kB into the TOC, so that 64kB > - * of the TOC can be addressed using a single machine instruction. > - */ > +void __init initialise_paca(struct paca_struct *new_paca, int cpu) > +{ > + /* The TOC register (GPR2) points 32kB into the TOC, so that 64kB > + * of the TOC can be addressed using a single machine instruction. > + */ > 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 > - new_paca->lppaca_ptr = &lppaca[cpu]; > + new_paca->lppaca_ptr = &lppaca[cpu]; > #else > - new_paca->kernel_pgd = swapper_pg_dir; > + new_paca->kernel_pgd = swapper_pg_dir; > #endif > - new_paca->lock_token = 0x8000; > - new_paca->paca_index = cpu; > - new_paca->kernel_toc = kernel_toc; > - new_paca->kernelbase = (unsigned long) _stext; > - new_paca->kernel_msr = MSR_KERNEL; > - new_paca->hw_cpu_id = 0xffff; > - new_paca->__current = &init_task; > + new_paca->lock_token = 0x8000; > + new_paca->paca_index = cpu; > + new_paca->kernel_toc = kernel_toc; > + new_paca->kernelbase = (unsigned long) _stext; > + new_paca->kernel_msr = MSR_KERNEL; > + new_paca->hw_cpu_id = 0xffff; > + new_paca->__current = &init_task; > #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 */ > +} > + > +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; > } > diff --git a/arch/powerpc/kernel/prom.c b/arch/powerpc/kernel/prom.c > index 4ec3008..50c3c55 100644 > --- a/arch/powerpc/kernel/prom.c > +++ b/arch/powerpc/kernel/prom.c > @@ -1224,6 +1224,8 @@ void __init early_init_devtree(void *params) > * FIXME .. and the initrd too? */ > move_device_tree(); > > + allocate_pacas(); > + > DBG("Scanning CPUs ...\n"); > > /* Retreive CPU related informations from the flat tree > diff --git a/arch/powerpc/kernel/setup-common.c b/arch/powerpc/kernel/setup-c ommon.c > index 03dd6a2..5df0f6a 100644 > --- a/arch/powerpc/kernel/setup-common.c > +++ b/arch/powerpc/kernel/setup-common.c > @@ -493,6 +493,8 @@ void __init smp_setup_cpu_maps(void) > * here will have to be reworked > */ > cpu_init_thread_core_maps(nthreads); > + > + free_unused_pacas(); > } > #endif /* CONFIG_SMP */ > > diff --git a/arch/powerpc/kernel/setup_64.c b/arch/powerpc/kernel/setup_64.c > index 6568406..6354739 100644 > --- a/arch/powerpc/kernel/setup_64.c > +++ b/arch/powerpc/kernel/setup_64.c > @@ -144,9 +144,9 @@ early_param("smt-enabled", early_smt_enabled); > #endif /* CONFIG_SMP */ > > /* 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); > #ifdef CONFIG_PPC_BOOK3E > mtspr(SPRN_SPRG_TLB_EXFRAME, local_paca->extlb); > @@ -176,14 +176,12 @@ void __init early_setup(unsigned long dt_ptr) > { > /* -------- printk is _NOT_ safe to use here ! ------- */ > > - /* Fill in any unititialised pacas */ > - initialise_pacas(); > - > /* Identify CPU type */ > identify_cpu(0, mfspr(SPRN_PVR)); > > /* 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 */ > lockdep_init(); > @@ -203,7 +201,7 @@ void __init early_setup(unsigned long dt_ptr) > early_init_devtree(__va(dt_ptr)); > > /* 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 */ > get_paca()->cpu_start = 1; > diff --git a/arch/powerpc/platforms/iseries/exception.S b/arch/powerpc/platfo rms/iseries/exception.S > index 5369653..fba5bf9 100644 > --- a/arch/powerpc/platforms/iseries/exception.S > +++ b/arch/powerpc/platforms/iseries/exception.S > @@ -43,17 +43,14 @@ system_reset_iSeries: > LOAD_REG_ADDR(r23, alpaca) > li r0,ALPACA_SIZE > sub r23,r13,r23 > - divdu r23,r23,r0 /* r23 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 > + divdu r24,r23,r0 /* r24 has cpu number */ > cmpwi 0,r24,0 /* Are we processor 0? */ > 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 */ > 1: mfspr r4,SPRN_CTRLF > li r5,CTRL_RUNLATCH /* Turn off the run light */ > @@ -86,6 +83,16 @@ system_reset_iSeries: > #endif > > 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 > #ifdef CONFIG_SMP > lbz r23,PACAPROCSTART(r13) /* Test if this processor > -- > 1.6.3.3 > > _______________________________________________ > Linuxppc-dev mailing list > Linuxppc-dev@lists.ozlabs.org > https://lists.ozlabs.org/listinfo/linuxppc-dev > _______________________________________________ Linuxppc-dev mailing list Linuxppc-dev@lists.ozlabs.org https://lists.ozlabs.org/listinfo/linuxppc-dev