aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/kernel/process.c
diff options
context:
space:
mode:
authorNathan Lynch <nathan_lynch@mentor.com>2015-03-25 14:15:08 -0400
committerRussell King <rmk+kernel@arm.linux.org.uk>2015-03-27 18:20:45 -0400
commitecf99a439105ebd0a507af1a9cd901a2e166bf9a (patch)
treec24009696e0cd598a68eac95d7a512ff84e3da0a /arch/arm/kernel/process.c
parent8512287a8165592466cb9cb347ba94892e9c56a5 (diff)
ARM: 8331/1: VDSO initialization, mapping, and synchronization
Initialize the VDSO page list at boot, install the VDSO mapping at exec time, and update the data page during timer ticks. This code is not built if CONFIG_VDSO is not enabled. Account for the VDSO length when randomizing the offset from the stack. The [vdso] and [vvar] pages are placed immediately following the sigpage with separate _install_special_mapping calls. We want to "penalize" systems lacking the arch timer as little as possible. Previous versions of this code installed the VDSO unconditionally and unmodified, making it a measurably slower way for glibc to invoke the real syscalls on such systems. E.g. calling gettimeofday via glibc goes from ~560ns to ~630ns on i.MX6Q. If we can indicate to glibc that the time-related APIs in the VDSO are not accelerated, glibc can continue to invoke the syscalls directly instead of dispatching through the VDSO only to fall back to the slow path. Thus, if the architected timer is unusable for whatever reason, patch the VDSO at boot time so that symbol lookups for gettimeofday and clock_gettime return NULL. (This is similar to what powerpc does and borrows code from there.) This allows glibc to perform the syscall directly instead of passing control to the VDSO, which minimizes the penalty. In my measurements the time taken for a gettimeofday call via glibc goes from ~560ns to ~580ns (again on i.MX6Q), and this is solely due to adding a test and branch to glibc's gettimeofday syscall wrapper. An alternative to patching the VDSO at boot would be to not install the VDSO at all when the arch timer isn't usable. Another alternative is to include a separate "dummy" vdso.so without gettimeofday and clock_gettime, which would be selected at boot time. Either of these would get cumbersome if the VDSO were to gain support for an API such as getcpu which is unrelated to arch timer support. Signed-off-by: Nathan Lynch <nathan_lynch@mentor.com> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
Diffstat (limited to 'arch/arm/kernel/process.c')
-rw-r--r--arch/arm/kernel/process.c17
1 files changed, 14 insertions, 3 deletions
diff --git a/arch/arm/kernel/process.c b/arch/arm/kernel/process.c
index fdfa3a78ec8c..c50fe212fd89 100644
--- a/arch/arm/kernel/process.c
+++ b/arch/arm/kernel/process.c
@@ -41,6 +41,7 @@
41#include <asm/system_misc.h> 41#include <asm/system_misc.h>
42#include <asm/mach/time.h> 42#include <asm/mach/time.h>
43#include <asm/tls.h> 43#include <asm/tls.h>
44#include <asm/vdso.h>
44 45
45#ifdef CONFIG_CC_STACKPROTECTOR 46#ifdef CONFIG_CC_STACKPROTECTOR
46#include <linux/stackprotector.h> 47#include <linux/stackprotector.h>
@@ -475,7 +476,7 @@ const char *arch_vma_name(struct vm_area_struct *vma)
475} 476}
476 477
477/* If possible, provide a placement hint at a random offset from the 478/* If possible, provide a placement hint at a random offset from the
478 * stack for the signal page. 479 * stack for the sigpage and vdso pages.
479 */ 480 */
480static unsigned long sigpage_addr(const struct mm_struct *mm, 481static unsigned long sigpage_addr(const struct mm_struct *mm,
481 unsigned int npages) 482 unsigned int npages)
@@ -519,6 +520,7 @@ int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp)
519{ 520{
520 struct mm_struct *mm = current->mm; 521 struct mm_struct *mm = current->mm;
521 struct vm_area_struct *vma; 522 struct vm_area_struct *vma;
523 unsigned long npages;
522 unsigned long addr; 524 unsigned long addr;
523 unsigned long hint; 525 unsigned long hint;
524 int ret = 0; 526 int ret = 0;
@@ -528,9 +530,12 @@ int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp)
528 if (!signal_page) 530 if (!signal_page)
529 return -ENOMEM; 531 return -ENOMEM;
530 532
533 npages = 1; /* for sigpage */
534 npages += vdso_total_pages;
535
531 down_write(&mm->mmap_sem); 536 down_write(&mm->mmap_sem);
532 hint = sigpage_addr(mm, 1); 537 hint = sigpage_addr(mm, npages);
533 addr = get_unmapped_area(NULL, hint, PAGE_SIZE, 0, 0); 538 addr = get_unmapped_area(NULL, hint, npages << PAGE_SHIFT, 0, 0);
534 if (IS_ERR_VALUE(addr)) { 539 if (IS_ERR_VALUE(addr)) {
535 ret = addr; 540 ret = addr;
536 goto up_fail; 541 goto up_fail;
@@ -547,6 +552,12 @@ int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp)
547 552
548 mm->context.sigpage = addr; 553 mm->context.sigpage = addr;
549 554
555 /* Unlike the sigpage, failure to install the vdso is unlikely
556 * to be fatal to the process, so no error check needed
557 * here.
558 */
559 arm_install_vdso(mm, addr + PAGE_SIZE);
560
550 up_fail: 561 up_fail:
551 up_write(&mm->mmap_sem); 562 up_write(&mm->mmap_sem);
552 return ret; 563 return ret;