mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-08-05 16:54:27 +00:00
powerpc/vdso: Add support for time namespaces
This patch adds the necessary glue to provide time namespaces. Things are mainly copied from ARM64. __arch_get_timens_vdso_data() calculates timens vdso data position based on the vdso data position, knowing it is the next page in vvar. This avoids having to redo the mflr/bcl/mflr/mtlr dance to locate the page relative to running code position. Signed-off-by: Christophe Leroy <christophe.leroy@csgroup.eu> Reviewed-by: Vincenzo Frascino <vincenzo.frascino@arm.com> # vDSO parts Acked-by: Andrei Vagin <avagin@gmail.com> Signed-off-by: Michael Ellerman <mpe@ellerman.id.au> Link: https://lore.kernel.org/r/1a15495f80ec19a87b16cf874dbf7c3fa5ec40fe.1617209142.git.christophe.leroy@csgroup.eu
This commit is contained in:
parent
1c4bce6753
commit
74205b3fc2
6 changed files with 114 additions and 21 deletions
|
@ -173,6 +173,7 @@ config PPC
|
||||||
select GENERIC_CPU_AUTOPROBE
|
select GENERIC_CPU_AUTOPROBE
|
||||||
select GENERIC_CPU_VULNERABILITIES if PPC_BARRIER_NOSPEC
|
select GENERIC_CPU_VULNERABILITIES if PPC_BARRIER_NOSPEC
|
||||||
select GENERIC_EARLY_IOREMAP
|
select GENERIC_EARLY_IOREMAP
|
||||||
|
select GENERIC_GETTIMEOFDAY
|
||||||
select GENERIC_IRQ_SHOW
|
select GENERIC_IRQ_SHOW
|
||||||
select GENERIC_IRQ_SHOW_LEVEL
|
select GENERIC_IRQ_SHOW_LEVEL
|
||||||
select GENERIC_PCI_IOMAP if PCI
|
select GENERIC_PCI_IOMAP if PCI
|
||||||
|
@ -180,7 +181,7 @@ config PPC
|
||||||
select GENERIC_STRNCPY_FROM_USER
|
select GENERIC_STRNCPY_FROM_USER
|
||||||
select GENERIC_STRNLEN_USER
|
select GENERIC_STRNLEN_USER
|
||||||
select GENERIC_TIME_VSYSCALL
|
select GENERIC_TIME_VSYSCALL
|
||||||
select GENERIC_GETTIMEOFDAY
|
select GENERIC_VDSO_TIME_NS
|
||||||
select HAVE_ARCH_AUDITSYSCALL
|
select HAVE_ARCH_AUDITSYSCALL
|
||||||
select HAVE_ARCH_HUGE_VMAP if PPC_BOOK3S_64 && PPC_RADIX_MMU
|
select HAVE_ARCH_HUGE_VMAP if PPC_BOOK3S_64 && PPC_RADIX_MMU
|
||||||
select HAVE_ARCH_JUMP_LABEL
|
select HAVE_ARCH_JUMP_LABEL
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
#ifndef _ASM_POWERPC_VDSO_GETTIMEOFDAY_H
|
#ifndef _ASM_POWERPC_VDSO_GETTIMEOFDAY_H
|
||||||
#define _ASM_POWERPC_VDSO_GETTIMEOFDAY_H
|
#define _ASM_POWERPC_VDSO_GETTIMEOFDAY_H
|
||||||
|
|
||||||
|
#include <asm/page.h>
|
||||||
|
|
||||||
#ifdef __ASSEMBLY__
|
#ifdef __ASSEMBLY__
|
||||||
|
|
||||||
#include <asm/ppc_asm.h>
|
#include <asm/ppc_asm.h>
|
||||||
|
@ -154,6 +156,14 @@ static __always_inline u64 __arch_get_hw_counter(s32 clock_mode,
|
||||||
|
|
||||||
const struct vdso_data *__arch_get_vdso_data(void);
|
const struct vdso_data *__arch_get_vdso_data(void);
|
||||||
|
|
||||||
|
#ifdef CONFIG_TIME_NS
|
||||||
|
static __always_inline
|
||||||
|
const struct vdso_data *__arch_get_timens_vdso_data(const struct vdso_data *vd)
|
||||||
|
{
|
||||||
|
return (void *)vd + PAGE_SIZE;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
static inline bool vdso_clocksource_ok(const struct vdso_data *vd)
|
static inline bool vdso_clocksource_ok(const struct vdso_data *vd)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -107,9 +107,7 @@ extern struct vdso_arch_data *vdso_data;
|
||||||
bcl 20, 31, .+4
|
bcl 20, 31, .+4
|
||||||
999:
|
999:
|
||||||
mflr \ptr
|
mflr \ptr
|
||||||
#if CONFIG_PPC_PAGE_SHIFT > 14
|
|
||||||
addis \ptr, \ptr, (_vdso_datapage - 999b)@ha
|
addis \ptr, \ptr, (_vdso_datapage - 999b)@ha
|
||||||
#endif
|
|
||||||
addi \ptr, \ptr, (_vdso_datapage - 999b)@l
|
addi \ptr, \ptr, (_vdso_datapage - 999b)@l
|
||||||
.endm
|
.endm
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
#include <linux/security.h>
|
#include <linux/security.h>
|
||||||
#include <linux/memblock.h>
|
#include <linux/memblock.h>
|
||||||
#include <linux/syscalls.h>
|
#include <linux/syscalls.h>
|
||||||
|
#include <linux/time_namespace.h>
|
||||||
#include <vdso/datapage.h>
|
#include <vdso/datapage.h>
|
||||||
|
|
||||||
#include <asm/syscall.h>
|
#include <asm/syscall.h>
|
||||||
|
@ -50,6 +51,12 @@ static union {
|
||||||
} vdso_data_store __page_aligned_data;
|
} vdso_data_store __page_aligned_data;
|
||||||
struct vdso_arch_data *vdso_data = &vdso_data_store.data;
|
struct vdso_arch_data *vdso_data = &vdso_data_store.data;
|
||||||
|
|
||||||
|
enum vvar_pages {
|
||||||
|
VVAR_DATA_PAGE_OFFSET,
|
||||||
|
VVAR_TIMENS_PAGE_OFFSET,
|
||||||
|
VVAR_NR_PAGES,
|
||||||
|
};
|
||||||
|
|
||||||
static int vdso_mremap(const struct vm_special_mapping *sm, struct vm_area_struct *new_vma,
|
static int vdso_mremap(const struct vm_special_mapping *sm, struct vm_area_struct *new_vma,
|
||||||
unsigned long text_size)
|
unsigned long text_size)
|
||||||
{
|
{
|
||||||
|
@ -73,8 +80,12 @@ static int vdso64_mremap(const struct vm_special_mapping *sm, struct vm_area_str
|
||||||
return vdso_mremap(sm, new_vma, &vdso64_end - &vdso64_start);
|
return vdso_mremap(sm, new_vma, &vdso64_end - &vdso64_start);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static vm_fault_t vvar_fault(const struct vm_special_mapping *sm,
|
||||||
|
struct vm_area_struct *vma, struct vm_fault *vmf);
|
||||||
|
|
||||||
static struct vm_special_mapping vvar_spec __ro_after_init = {
|
static struct vm_special_mapping vvar_spec __ro_after_init = {
|
||||||
.name = "[vvar]",
|
.name = "[vvar]",
|
||||||
|
.fault = vvar_fault,
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct vm_special_mapping vdso32_spec __ro_after_init = {
|
static struct vm_special_mapping vdso32_spec __ro_after_init = {
|
||||||
|
@ -87,6 +98,94 @@ static struct vm_special_mapping vdso64_spec __ro_after_init = {
|
||||||
.mremap = vdso64_mremap,
|
.mremap = vdso64_mremap,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#ifdef CONFIG_TIME_NS
|
||||||
|
struct vdso_data *arch_get_vdso_data(void *vvar_page)
|
||||||
|
{
|
||||||
|
return ((struct vdso_arch_data *)vvar_page)->data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The vvar mapping contains data for a specific time namespace, so when a task
|
||||||
|
* changes namespace we must unmap its vvar data for the old namespace.
|
||||||
|
* Subsequent faults will map in data for the new namespace.
|
||||||
|
*
|
||||||
|
* For more details see timens_setup_vdso_data().
|
||||||
|
*/
|
||||||
|
int vdso_join_timens(struct task_struct *task, struct time_namespace *ns)
|
||||||
|
{
|
||||||
|
struct mm_struct *mm = task->mm;
|
||||||
|
struct vm_area_struct *vma;
|
||||||
|
|
||||||
|
mmap_read_lock(mm);
|
||||||
|
|
||||||
|
for (vma = mm->mmap; vma; vma = vma->vm_next) {
|
||||||
|
unsigned long size = vma->vm_end - vma->vm_start;
|
||||||
|
|
||||||
|
if (vma_is_special_mapping(vma, &vvar_spec))
|
||||||
|
zap_page_range(vma, vma->vm_start, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
mmap_read_unlock(mm);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct page *find_timens_vvar_page(struct vm_area_struct *vma)
|
||||||
|
{
|
||||||
|
if (likely(vma->vm_mm == current->mm))
|
||||||
|
return current->nsproxy->time_ns->vvar_page;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* VM_PFNMAP | VM_IO protect .fault() handler from being called
|
||||||
|
* through interfaces like /proc/$pid/mem or
|
||||||
|
* process_vm_{readv,writev}() as long as there's no .access()
|
||||||
|
* in special_mapping_vmops.
|
||||||
|
* For more details check_vma_flags() and __access_remote_vm()
|
||||||
|
*/
|
||||||
|
WARN(1, "vvar_page accessed remotely");
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
static struct page *find_timens_vvar_page(struct vm_area_struct *vma)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static vm_fault_t vvar_fault(const struct vm_special_mapping *sm,
|
||||||
|
struct vm_area_struct *vma, struct vm_fault *vmf)
|
||||||
|
{
|
||||||
|
struct page *timens_page = find_timens_vvar_page(vma);
|
||||||
|
unsigned long pfn;
|
||||||
|
|
||||||
|
switch (vmf->pgoff) {
|
||||||
|
case VVAR_DATA_PAGE_OFFSET:
|
||||||
|
if (timens_page)
|
||||||
|
pfn = page_to_pfn(timens_page);
|
||||||
|
else
|
||||||
|
pfn = virt_to_pfn(vdso_data);
|
||||||
|
break;
|
||||||
|
#ifdef CONFIG_TIME_NS
|
||||||
|
case VVAR_TIMENS_PAGE_OFFSET:
|
||||||
|
/*
|
||||||
|
* If a task belongs to a time namespace then a namespace
|
||||||
|
* specific VVAR is mapped with the VVAR_DATA_PAGE_OFFSET and
|
||||||
|
* the real VVAR page is mapped with the VVAR_TIMENS_PAGE_OFFSET
|
||||||
|
* offset.
|
||||||
|
* See also the comment near timens_setup_vdso_data().
|
||||||
|
*/
|
||||||
|
if (!timens_page)
|
||||||
|
return VM_FAULT_SIGBUS;
|
||||||
|
pfn = virt_to_pfn(vdso_data);
|
||||||
|
break;
|
||||||
|
#endif /* CONFIG_TIME_NS */
|
||||||
|
default:
|
||||||
|
return VM_FAULT_SIGBUS;
|
||||||
|
}
|
||||||
|
|
||||||
|
return vmf_insert_pfn(vma, vmf->address, pfn);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This is called from binfmt_elf, we create the special vma for the
|
* This is called from binfmt_elf, we create the special vma for the
|
||||||
* vDSO and insert it into the mm struct tree
|
* vDSO and insert it into the mm struct tree
|
||||||
|
@ -95,7 +194,7 @@ static int __arch_setup_additional_pages(struct linux_binprm *bprm, int uses_int
|
||||||
{
|
{
|
||||||
unsigned long vdso_size, vdso_base, mappings_size;
|
unsigned long vdso_size, vdso_base, mappings_size;
|
||||||
struct vm_special_mapping *vdso_spec;
|
struct vm_special_mapping *vdso_spec;
|
||||||
unsigned long vvar_size = PAGE_SIZE;
|
unsigned long vvar_size = VVAR_NR_PAGES * PAGE_SIZE;
|
||||||
struct mm_struct *mm = current->mm;
|
struct mm_struct *mm = current->mm;
|
||||||
struct vm_area_struct *vma;
|
struct vm_area_struct *vma;
|
||||||
|
|
||||||
|
@ -266,19 +365,6 @@ static struct page ** __init vdso_setup_pages(void *start, void *end)
|
||||||
return pagelist;
|
return pagelist;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct page ** __init vvar_setup_pages(void)
|
|
||||||
{
|
|
||||||
struct page **pagelist;
|
|
||||||
|
|
||||||
/* .pages is NULL-terminated */
|
|
||||||
pagelist = kcalloc(2, sizeof(struct page *), GFP_KERNEL);
|
|
||||||
if (!pagelist)
|
|
||||||
panic("%s: Cannot allocate page list for VVAR", __func__);
|
|
||||||
|
|
||||||
pagelist[0] = virt_to_page(vdso_data);
|
|
||||||
return pagelist;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int __init vdso_init(void)
|
static int __init vdso_init(void)
|
||||||
{
|
{
|
||||||
#ifdef CONFIG_PPC64
|
#ifdef CONFIG_PPC64
|
||||||
|
@ -317,8 +403,6 @@ static int __init vdso_init(void)
|
||||||
if (IS_ENABLED(CONFIG_PPC64))
|
if (IS_ENABLED(CONFIG_PPC64))
|
||||||
vdso64_spec.pages = vdso_setup_pages(&vdso64_start, &vdso64_end);
|
vdso64_spec.pages = vdso_setup_pages(&vdso64_start, &vdso64_end);
|
||||||
|
|
||||||
vvar_spec.pages = vvar_setup_pages();
|
|
||||||
|
|
||||||
smp_wmb();
|
smp_wmb();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -17,7 +17,7 @@ ENTRY(_start)
|
||||||
|
|
||||||
SECTIONS
|
SECTIONS
|
||||||
{
|
{
|
||||||
PROVIDE(_vdso_datapage = . - PAGE_SIZE);
|
PROVIDE(_vdso_datapage = . - 2 * PAGE_SIZE);
|
||||||
. = SIZEOF_HEADERS;
|
. = SIZEOF_HEADERS;
|
||||||
|
|
||||||
.hash : { *(.hash) } :text
|
.hash : { *(.hash) } :text
|
||||||
|
|
|
@ -17,7 +17,7 @@ ENTRY(_start)
|
||||||
|
|
||||||
SECTIONS
|
SECTIONS
|
||||||
{
|
{
|
||||||
PROVIDE(_vdso_datapage = . - PAGE_SIZE);
|
PROVIDE(_vdso_datapage = . - 2 * PAGE_SIZE);
|
||||||
. = SIZEOF_HEADERS;
|
. = SIZEOF_HEADERS;
|
||||||
|
|
||||||
.hash : { *(.hash) } :text
|
.hash : { *(.hash) } :text
|
||||||
|
|
Loading…
Add table
Reference in a new issue