diff options
Diffstat (limited to 'arch/powerpc/kernel/vdso.c')
| -rw-r--r-- | arch/powerpc/kernel/vdso.c | 102 |
1 files changed, 72 insertions, 30 deletions
diff --git a/arch/powerpc/kernel/vdso.c b/arch/powerpc/kernel/vdso.c index ad06d5c75b1..ce74c335a6a 100644 --- a/arch/powerpc/kernel/vdso.c +++ b/arch/powerpc/kernel/vdso.c @@ -1,3 +1,4 @@ + /* * Copyright (C) 2004 Benjamin Herrenschmidt, IBM Corp. * <benh@kernel.crashing.org> @@ -8,7 +9,6 @@ * 2 of the License, or (at your option) any later version. */ -#include <linux/module.h> #include <linux/errno.h> #include <linux/sched.h> #include <linux/kernel.h> @@ -21,10 +21,9 @@ #include <linux/elf.h> #include <linux/security.h> #include <linux/bootmem.h> -#include <linux/lmb.h> +#include <linux/memblock.h> #include <asm/pgtable.h> -#include <asm/system.h> #include <asm/processor.h> #include <asm/mmu.h> #include <asm/mmu_context.h> @@ -35,8 +34,7 @@ #include <asm/firmware.h> #include <asm/vdso.h> #include <asm/vdso_datapage.h> - -#include "setup.h" +#include <asm/setup.h> #undef DEBUG @@ -49,6 +47,9 @@ /* Max supported size for symbol names */ #define MAX_SYMNAME 64 +/* The alignment of the vDSO */ +#define VDSO_ALIGNMENT (1 << 16) + extern char vdso32_start, vdso32_end; static void *vdso32_kbase = &vdso32_start; static unsigned int vdso32_pages; @@ -74,7 +75,7 @@ static int vdso_ready; static union { struct vdso_data data; u8 page[PAGE_SIZE]; -} vdso_data_store __attribute__((__section__(".data.page_aligned"))); +} vdso_data_store __page_aligned_data; struct vdso_data *vdso_data = &vdso_data_store.data; /* Format of the patch table */ @@ -111,6 +112,10 @@ static struct vdso_patch_def vdso_patches[] = { CPU_FTR_USE_TB, 0, "__kernel_get_tbfreq", NULL }, + { + CPU_FTR_USE_TB, 0, + "__kernel_time", NULL + }, }; /* @@ -155,7 +160,7 @@ static void dump_vdso_pages(struct vm_area_struct * vma) { int i; - if (!vma || test_thread_flag(TIF_32BIT)) { + if (!vma || is_32bit_task()) { printk("vDSO32 @ %016lx:\n", (unsigned long)vdso32_kbase); for (i=0; i<vdso32_pages; i++) { struct page *pg = virt_to_page(vdso32_kbase + @@ -166,7 +171,7 @@ static void dump_vdso_pages(struct vm_area_struct * vma) dump_one_vdso_page(pg, upg); } } - if (!vma || !test_thread_flag(TIF_32BIT)) { + if (!vma || !is_32bit_task()) { printk("vDSO64 @ %016lx:\n", (unsigned long)vdso64_kbase); for (i=0; i<vdso64_pages; i++) { struct page *pg = virt_to_page(vdso64_kbase + @@ -196,14 +201,19 @@ int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp) return 0; #ifdef CONFIG_PPC64 - if (test_thread_flag(TIF_32BIT)) { + if (is_32bit_task()) { vdso_pagelist = vdso32_pagelist; vdso_pages = vdso32_pages; vdso_base = VDSO32_MBASE; } else { vdso_pagelist = vdso64_pagelist; vdso_pages = vdso64_pages; - vdso_base = VDSO64_MBASE; + /* + * On 64bit we don't have a preferred map address. This + * allows get_unmapped_area to find an area near other mmaps + * and most likely share a SLB entry. + */ + vdso_base = 0; } #else vdso_pagelist = vdso32_pagelist; @@ -225,15 +235,28 @@ int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp) * pick a base address for the vDSO in process space. We try to put it * at vdso_base which is the "natural" base for it, but we might fail * and end up putting it elsewhere. + * Add enough to the size so that the result can be aligned. */ down_write(&mm->mmap_sem); vdso_base = get_unmapped_area(NULL, vdso_base, - vdso_pages << PAGE_SHIFT, 0, 0); + (vdso_pages << PAGE_SHIFT) + + ((VDSO_ALIGNMENT - 1) & PAGE_MASK), + 0, 0); if (IS_ERR_VALUE(vdso_base)) { rc = vdso_base; goto fail_mmapsem; } + /* Add required alignment. */ + vdso_base = ALIGN(vdso_base, VDSO_ALIGNMENT); + + /* + * Put vDSO base into mm struct. We need to do this before calling + * install_special_mapping or the perf counter mmap tracking code + * will fail to recognise it as a vDSO (since arch_vma_name fails). + */ + current->mm->context.vdso_base = vdso_base; + /* * our vma flags don't have VM_WRITE so by default, the process isn't * allowed to write those pages. @@ -242,23 +265,16 @@ int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp) * the "data" page of the vDSO or you'll stop getting kernel updates * and your nice userland gettimeofday will be totally dead. * It's fine to use that for setting breakpoints in the vDSO code - * pages though - * - * Make sure the vDSO gets into every core dump. - * Dumping its contents makes post-mortem fully interpretable later - * without matching up the same kernel and hardware config to see - * what PC values meant. + * pages though. */ rc = install_special_mapping(mm, vdso_base, vdso_pages << PAGE_SHIFT, VM_READ|VM_EXEC| - VM_MAYREAD|VM_MAYWRITE|VM_MAYEXEC| - VM_ALWAYSDUMP, + VM_MAYREAD|VM_MAYWRITE|VM_MAYEXEC, vdso_pagelist); - if (rc) + if (rc) { + current->mm->context.vdso_base = 0; goto fail_mmapsem; - - /* Put vDSO base into mm struct */ - current->mm->context.vdso_base = vdso_base; + } up_write(&mm->mmap_sem); return 0; @@ -693,6 +709,32 @@ static void __init vdso_setup_syscall_map(void) } } +#ifdef CONFIG_PPC64 +int vdso_getcpu_init(void) +{ + unsigned long cpu, node, val; + + /* + * SPRG_VDSO contains the CPU in the bottom 16 bits and the NUMA node + * in the next 16 bits. The VDSO uses this to implement getcpu(). + */ + cpu = get_cpu(); + WARN_ON_ONCE(cpu > 0xffff); + + node = cpu_to_node(cpu); + WARN_ON_ONCE(node > 0xffff); + + val = (cpu & 0xfff) | ((node & 0xffff) << 16); + mtspr(SPRN_SPRG_VDSO_WRITE, val); + get_paca()->sprg_vdso = val; + + put_cpu(); + + return 0; +} +/* We need to call this before SMP init */ +early_initcall(vdso_getcpu_init); +#endif static int __init vdso_init(void) { @@ -700,20 +742,20 @@ static int __init vdso_init(void) #ifdef CONFIG_PPC64 /* - * Fill up the "systemcfg" stuff for backward compatiblity + * Fill up the "systemcfg" stuff for backward compatibility */ strcpy((char *)vdso_data->eye_catcher, "SYSTEMCFG:PPC64"); vdso_data->version.major = SYSTEMCFG_MAJOR; vdso_data->version.minor = SYSTEMCFG_MINOR; vdso_data->processor = mfspr(SPRN_PVR); /* - * Fake the old platform number for pSeries and iSeries and add + * Fake the old platform number for pSeries and add * in LPAR bit if necessary */ - vdso_data->platform = machine_is(iseries) ? 0x200 : 0x100; + vdso_data->platform = 0x100; if (firmware_has_feature(FW_FEATURE_LPAR)) vdso_data->platform |= 1; - vdso_data->physicalMemorySize = lmb_phys_mem_size(); + vdso_data->physicalMemorySize = memblock_phys_mem_size(); vdso_data->dcache_size = ppc64_caches.dsize; vdso_data->dcache_line_size = ppc64_caches.dline_size; vdso_data->icache_size = ppc64_caches.isize; @@ -799,17 +841,17 @@ static int __init vdso_init(void) } arch_initcall(vdso_init); -int in_gate_area_no_task(unsigned long addr) +int in_gate_area_no_mm(unsigned long addr) { return 0; } -int in_gate_area(struct task_struct *task, unsigned long addr) +int in_gate_area(struct mm_struct *mm, unsigned long addr) { return 0; } -struct vm_area_struct *get_gate_vma(struct task_struct *tsk) +struct vm_area_struct *get_gate_vma(struct mm_struct *mm) { return NULL; } |
