mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-01-04 04:04:19 +00:00
0352387523
The VDSO data page handling is architecture specific for historical reasons, but there is no real technical reason to do so. Aside of that VDSO data has become a dump ground for various mechanisms and fail to provide a clear separation of the functionalities. Clean this up by: * consolidating the VDSO page data by getting rid of architecture specific warts especially in x86 and PowerPC. * removing the last includes of header files which are pulling in other headers outside of the VDSO namespace. * seperating timekeeping and other VDSO data accordingly. Further consolidation of the VDSO page handling is done in subsequent changes scheduled for the next merge window. This also lays the ground for expanding the VDSO time getters for independent PTP clocks in a generic way without making every architecture add support seperately. -----BEGIN PGP SIGNATURE----- iQJHBAABCgAxFiEEQp8+kY+LLUocC4bMphj1TA10mKEFAmc7kyoTHHRnbHhAbGlu dXRyb25peC5kZQAKCRCmGPVMDXSYoVBjD/9awdN2YeCGIM9rlHIktUdNRmRSL2SL 6av1CPffN5DenONYTXWrDYPkC4yfjUwIs8H57uzFo10yA7RQ/Qfq+O68k5GnuFew jvpmmYSZ6TT21AmAaCIhn+kdl9YbEJFvN2AWH85Bl29k9FGB04VzJlQMMjfEZ1a5 Mhwv+cfYNuPSZmU570jcxW2XgbyTWlLZBByXX/Tuz9bwpmtszba507bvo45x6gIP twaWNzrsyJpdXfMrfUnRiChN8jHlDN7I6fgQvpsoRH5FOiVwIFo0Ip2rKbk+ONfD W/rcU5oeqRIxRVDHzf2Sv8WPHMCLRv01ZHBcbJOtgvZC3YiKgKYoeEKabu9ZL1BH 6VmrxjYOBBFQHOYAKPqBuS7BgH5PmtMbDdSZXDfRaAKaCzhCRysdlWW7z48r2R// zPufb7J6Tle23AkuZWhFjvlGgSBl4zxnTFn31HYOyQps3TMI4y50Z2DhE/EeU8a6 DRl8/k1KQVDUZ6udJogS5kOr1J8pFtUPrA2uhR8UyLdx7YKiCzcdO1qWAjtXlVe8 oNpzinU+H9bQqGe9IyS7kCG9xNaCRZNkln5Q1WfnkTzg5f6ihfaCvIku3l4bgVpw 3HmcxYiC6RxQB+ozwN7hzCCKT4L9aMhr/457TNOqRkj2Elw3nvJ02L4aI86XAKLE jwO9Fkp9qcCxCw== =q5eD -----END PGP SIGNATURE----- Merge tag 'timers-vdso-2024-11-18' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip Pull vdso data page handling updates from Thomas Gleixner: "First steps of consolidating the VDSO data page handling. The VDSO data page handling is architecture specific for historical reasons, but there is no real technical reason to do so. Aside of that VDSO data has become a dump ground for various mechanisms and fail to provide a clear separation of the functionalities. Clean this up by: - consolidating the VDSO page data by getting rid of architecture specific warts especially in x86 and PowerPC. - removing the last includes of header files which are pulling in other headers outside of the VDSO namespace. - seperating timekeeping and other VDSO data accordingly. Further consolidation of the VDSO page handling is done in subsequent changes scheduled for the next merge window. This also lays the ground for expanding the VDSO time getters for independent PTP clocks in a generic way without making every architecture add support seperately" * tag 'timers-vdso-2024-11-18' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (42 commits) x86/vdso: Add missing brackets in switch case vdso: Rename struct arch_vdso_data to arch_vdso_time_data powerpc: Split systemcfg struct definitions out from vdso powerpc: Split systemcfg data out of vdso data page powerpc: Add kconfig option for the systemcfg page powerpc/pseries/lparcfg: Use num_possible_cpus() for potential processors powerpc/pseries/lparcfg: Fix printing of system_active_processors powerpc/procfs: Propagate error of remap_pfn_range() powerpc/vdso: Remove offset comment from 32bit vdso_arch_data x86/vdso: Split virtual clock pages into dedicated mapping x86/vdso: Delete vvar.h x86/vdso: Access vdso data without vvar.h x86/vdso: Move the rng offset to vsyscall.h x86/vdso: Access rng vdso data without vvar.h x86/vdso: Access timens vdso data without vvar.h x86/vdso: Allocate vvar page from C code x86/vdso: Access rng data from kernel without vvar x86/vdso: Place vdso_data at beginning of vvar page x86/vdso: Use __arch_get_vdso_data() to access vdso data x86/mm/mmap: Remove arch_vma_name() ...
208 lines
5.0 KiB
C
208 lines
5.0 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Author: Huacai Chen <chenhuacai@loongson.cn>
|
|
* Copyright (C) 2020-2022 Loongson Technology Corporation Limited
|
|
*/
|
|
|
|
#include <linux/binfmts.h>
|
|
#include <linux/elf.h>
|
|
#include <linux/err.h>
|
|
#include <linux/init.h>
|
|
#include <linux/ioport.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/mm.h>
|
|
#include <linux/random.h>
|
|
#include <linux/sched.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/time_namespace.h>
|
|
|
|
#include <asm/page.h>
|
|
#include <asm/vdso.h>
|
|
#include <vdso/helpers.h>
|
|
#include <vdso/vsyscall.h>
|
|
#include <vdso/datapage.h>
|
|
#include <generated/vdso-offsets.h>
|
|
|
|
extern char vdso_start[], vdso_end[];
|
|
|
|
/* Kernel-provided data used by the VDSO. */
|
|
static union vdso_data_store generic_vdso_data __page_aligned_data;
|
|
|
|
static union {
|
|
u8 page[LOONGARCH_VDSO_DATA_SIZE];
|
|
struct loongarch_vdso_data vdata;
|
|
} loongarch_vdso_data __page_aligned_data;
|
|
|
|
struct vdso_data *vdso_data = generic_vdso_data.data;
|
|
struct vdso_pcpu_data *vdso_pdata = loongarch_vdso_data.vdata.pdata;
|
|
struct vdso_rng_data *vdso_rng_data = &loongarch_vdso_data.vdata.rng_data;
|
|
|
|
static int vdso_mremap(const struct vm_special_mapping *sm, struct vm_area_struct *new_vma)
|
|
{
|
|
current->mm->context.vdso = (void *)(new_vma->vm_start);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static vm_fault_t vvar_fault(const struct vm_special_mapping *sm,
|
|
struct vm_area_struct *vma, struct vm_fault *vmf)
|
|
{
|
|
unsigned long pfn;
|
|
struct page *timens_page = find_timens_vvar_page(vma);
|
|
|
|
switch (vmf->pgoff) {
|
|
case VVAR_GENERIC_PAGE_OFFSET:
|
|
if (!timens_page)
|
|
pfn = sym_to_pfn(vdso_data);
|
|
else
|
|
pfn = page_to_pfn(timens_page);
|
|
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_GENERIC_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;
|
|
else
|
|
pfn = sym_to_pfn(vdso_data);
|
|
break;
|
|
#endif /* CONFIG_TIME_NS */
|
|
case VVAR_LOONGARCH_PAGES_START ... VVAR_LOONGARCH_PAGES_END:
|
|
pfn = sym_to_pfn(&loongarch_vdso_data) + vmf->pgoff - VVAR_LOONGARCH_PAGES_START;
|
|
break;
|
|
default:
|
|
return VM_FAULT_SIGBUS;
|
|
}
|
|
|
|
return vmf_insert_pfn(vma, vmf->address, pfn);
|
|
}
|
|
|
|
struct loongarch_vdso_info vdso_info = {
|
|
.vdso = vdso_start,
|
|
.code_mapping = {
|
|
.name = "[vdso]",
|
|
.mremap = vdso_mremap,
|
|
},
|
|
.data_mapping = {
|
|
.name = "[vvar]",
|
|
.fault = vvar_fault,
|
|
},
|
|
.offset_sigreturn = vdso_offset_sigreturn,
|
|
};
|
|
|
|
static int __init init_vdso(void)
|
|
{
|
|
unsigned long i, cpu, pfn;
|
|
|
|
BUG_ON(!PAGE_ALIGNED(vdso_info.vdso));
|
|
|
|
for_each_possible_cpu(cpu)
|
|
vdso_pdata[cpu].node = cpu_to_node(cpu);
|
|
|
|
vdso_info.size = PAGE_ALIGN(vdso_end - vdso_start);
|
|
vdso_info.code_mapping.pages =
|
|
kcalloc(vdso_info.size / PAGE_SIZE, sizeof(struct page *), GFP_KERNEL);
|
|
|
|
pfn = __phys_to_pfn(__pa_symbol(vdso_info.vdso));
|
|
for (i = 0; i < vdso_info.size / PAGE_SIZE; i++)
|
|
vdso_info.code_mapping.pages[i] = pfn_to_page(pfn + i);
|
|
|
|
return 0;
|
|
}
|
|
subsys_initcall(init_vdso);
|
|
|
|
#ifdef CONFIG_TIME_NS
|
|
struct vdso_data *arch_get_vdso_data(void *vvar_page)
|
|
{
|
|
return (struct vdso_data *)(vvar_page);
|
|
}
|
|
|
|
/*
|
|
* 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;
|
|
|
|
VMA_ITERATOR(vmi, mm, 0);
|
|
|
|
mmap_read_lock(mm);
|
|
for_each_vma(vmi, vma) {
|
|
if (vma_is_special_mapping(vma, &vdso_info.data_mapping))
|
|
zap_vma_pages(vma);
|
|
}
|
|
mmap_read_unlock(mm);
|
|
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
static unsigned long vdso_base(void)
|
|
{
|
|
unsigned long base = STACK_TOP;
|
|
|
|
if (current->flags & PF_RANDOMIZE) {
|
|
base += get_random_u32_below(VDSO_RANDOMIZE_SIZE);
|
|
base = PAGE_ALIGN(base);
|
|
}
|
|
|
|
return base;
|
|
}
|
|
|
|
int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp)
|
|
{
|
|
int ret;
|
|
unsigned long size, data_addr, vdso_addr;
|
|
struct mm_struct *mm = current->mm;
|
|
struct vm_area_struct *vma;
|
|
struct loongarch_vdso_info *info = current->thread.vdso;
|
|
|
|
if (mmap_write_lock_killable(mm))
|
|
return -EINTR;
|
|
|
|
/*
|
|
* Determine total area size. This includes the VDSO data itself
|
|
* and the data pages.
|
|
*/
|
|
size = VVAR_SIZE + info->size;
|
|
|
|
data_addr = get_unmapped_area(NULL, vdso_base(), size, 0, 0);
|
|
if (IS_ERR_VALUE(data_addr)) {
|
|
ret = data_addr;
|
|
goto out;
|
|
}
|
|
|
|
vma = _install_special_mapping(mm, data_addr, VVAR_SIZE,
|
|
VM_READ | VM_MAYREAD | VM_PFNMAP,
|
|
&info->data_mapping);
|
|
if (IS_ERR(vma)) {
|
|
ret = PTR_ERR(vma);
|
|
goto out;
|
|
}
|
|
|
|
vdso_addr = data_addr + VVAR_SIZE;
|
|
vma = _install_special_mapping(mm, vdso_addr, info->size,
|
|
VM_READ | VM_EXEC | VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC,
|
|
&info->code_mapping);
|
|
if (IS_ERR(vma)) {
|
|
ret = PTR_ERR(vma);
|
|
goto out;
|
|
}
|
|
|
|
mm->context.vdso = (void *)vdso_addr;
|
|
ret = 0;
|
|
|
|
out:
|
|
mmap_write_unlock(mm);
|
|
return ret;
|
|
}
|