Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

arm64: vdso: Switch to generic storage implementation

The generic storage implementation provides the same features as the
custom one. However it can be shared between architectures, making
maintenance easier.

This switch also moves the random state data out of the time data page.
The currently used hardcoded __VDSO_RND_DATA_OFFSET does not take into
account changes to the time data page layout.

Co-developed-by: Nam Cao <namcao@linutronix.de>
Signed-off-by: Nam Cao <namcao@linutronix.de>
Signed-off-by: Thomas Weißschuh <thomas.weissschuh@linutronix.de>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Link: https://lore.kernel.org/all/20250204-vdso-store-rng-v3-8-13a4669dfc8c@linutronix.de

authored by

Thomas Weißschuh and committed by
Thomas Gleixner
0b3bc335 365841e1

+26 -170
+1
arch/arm64/Kconfig
··· 162 162 select GENERIC_SMP_IDLE_THREAD 163 163 select GENERIC_TIME_VSYSCALL 164 164 select GENERIC_GETTIMEOFDAY 165 + select GENERIC_VDSO_DATA_STORE 165 166 select GENERIC_VDSO_TIME_NS 166 167 select HARDIRQS_SW_RESEND 167 168 select HAS_IOPORT
+1 -1
arch/arm64/include/asm/vdso.h
··· 5 5 #ifndef __ASM_VDSO_H 6 6 #define __ASM_VDSO_H 7 7 8 - #define __VVAR_PAGES 2 8 + #define __VDSO_PAGES 4 9 9 10 10 #ifndef __ASSEMBLY__ 11 11
+12 -24
arch/arm64/include/asm/vdso/compat_gettimeofday.h
··· 104 104 } 105 105 106 106 static __always_inline u64 __arch_get_hw_counter(s32 clock_mode, 107 - const struct vdso_data *vd) 107 + const struct vdso_time_data *vd) 108 108 { 109 109 u64 res; 110 110 ··· 131 131 return res; 132 132 } 133 133 134 - static __always_inline const struct vdso_data *__arch_get_vdso_data(void) 134 + static __always_inline const struct vdso_time_data *__arch_get_vdso_u_time_data(void) 135 135 { 136 - const struct vdso_data *ret; 136 + const struct vdso_time_data *ret; 137 137 138 138 /* 139 - * This simply puts &_vdso_data into ret. The reason why we don't use 140 - * `ret = _vdso_data` is that the compiler tends to optimise this in a 141 - * very suboptimal way: instead of keeping &_vdso_data in a register, 142 - * it goes through a relocation almost every time _vdso_data must be 139 + * This simply puts &_vdso_time_data into ret. The reason why we don't use 140 + * `ret = _vdso_time_data` is that the compiler tends to optimise this in a 141 + * very suboptimal way: instead of keeping &_vdso_time_data in a register, 142 + * it goes through a relocation almost every time _vdso_time_data must be 143 143 * accessed (even in subfunctions). This is both time and space 144 144 * consuming: each relocation uses a word in the code section, and it 145 145 * has to be loaded at runtime. 146 146 * 147 147 * This trick hides the assignment from the compiler. Since it cannot 148 148 * track where the pointer comes from, it will only use one relocation 149 - * where __arch_get_vdso_data() is called, and then keep the result in 150 - * a register. 149 + * where __aarch64_get_vdso_u_time_data() is called, and then keep the 150 + * result in a register. 151 151 */ 152 - asm volatile("mov %0, %1" : "=r"(ret) : "r"(_vdso_data)); 152 + asm volatile("mov %0, %1" : "=r"(ret) : "r"(vdso_u_time_data)); 153 153 154 154 return ret; 155 155 } 156 + #define __arch_get_vdso_u_time_data __arch_get_vdso_u_time_data 156 157 157 - #ifdef CONFIG_TIME_NS 158 - static __always_inline 159 - const struct vdso_data *__arch_get_timens_vdso_data(const struct vdso_data *vd) 160 - { 161 - const struct vdso_data *ret; 162 - 163 - /* See __arch_get_vdso_data(). */ 164 - asm volatile("mov %0, %1" : "=r"(ret) : "r"(_timens_data)); 165 - 166 - return ret; 167 - } 168 - #endif 169 - 170 - static inline bool vdso_clocksource_ok(const struct vdso_data *vd) 158 + static inline bool vdso_clocksource_ok(const struct vdso_time_data *vd) 171 159 { 172 160 return vd->clock_mode == VDSO_CLOCKMODE_ARCHTIMER; 173 161 }
-12
arch/arm64/include/asm/vdso/getrandom.h
··· 33 33 return ret; 34 34 } 35 35 36 - static __always_inline const struct vdso_rng_data *__arch_get_vdso_rng_data(void) 37 - { 38 - /* 39 - * The RNG data is in the real VVAR data page, but if a task belongs to a time namespace 40 - * then VVAR_DATA_PAGE_OFFSET points to the namespace-specific VVAR page and VVAR_TIMENS_ 41 - * PAGE_OFFSET points to the real VVAR page. 42 - */ 43 - if (IS_ENABLED(CONFIG_TIME_NS) && _vdso_data->clock_mode == VDSO_CLOCKMODE_TIMENS) 44 - return (void *)&_vdso_rng_data + VVAR_TIMENS_PAGE_OFFSET * (1UL << CONFIG_PAGE_SHIFT); 45 - return &_vdso_rng_data; 46 - } 47 - 48 36 #endif /* !__ASSEMBLY__ */ 49 37 50 38 #endif /* __ASM_VDSO_GETRANDOM_H */
+1 -15
arch/arm64/include/asm/vdso/gettimeofday.h
··· 67 67 } 68 68 69 69 static __always_inline u64 __arch_get_hw_counter(s32 clock_mode, 70 - const struct vdso_data *vd) 70 + const struct vdso_time_data *vd) 71 71 { 72 72 u64 res; 73 73 ··· 98 98 99 99 return res; 100 100 } 101 - 102 - static __always_inline 103 - const struct vdso_data *__arch_get_vdso_data(void) 104 - { 105 - return _vdso_data; 106 - } 107 - 108 - #ifdef CONFIG_TIME_NS 109 - static __always_inline 110 - const struct vdso_data *__arch_get_timens_vdso_data(const struct vdso_data *vd) 111 - { 112 - return _timens_data; 113 - } 114 - #endif 115 101 116 102 #endif /* !__ASSEMBLY__ */ 117 103
+1 -24
arch/arm64/include/asm/vdso/vsyscall.h
··· 2 2 #ifndef __ASM_VDSO_VSYSCALL_H 3 3 #define __ASM_VDSO_VSYSCALL_H 4 4 5 - #define __VDSO_RND_DATA_OFFSET 480 6 - 7 5 #ifndef __ASSEMBLY__ 8 6 9 7 #include <vdso/datapage.h> 10 8 11 - enum vvar_pages { 12 - VVAR_DATA_PAGE_OFFSET, 13 - VVAR_TIMENS_PAGE_OFFSET, 14 - VVAR_NR_PAGES, 15 - }; 16 - 17 9 #define VDSO_PRECISION_MASK ~(0xFF00ULL<<48) 18 10 19 - extern struct vdso_data *vdso_data; 20 11 21 12 /* 22 13 * Update the vDSO data page to keep in sync with kernel timekeeping. 23 14 */ 24 15 static __always_inline 25 - struct vdso_data *__arm64_get_k_vdso_data(void) 26 - { 27 - return vdso_data; 28 - } 29 - #define __arch_get_k_vdso_data __arm64_get_k_vdso_data 30 - 31 - static __always_inline 32 - struct vdso_rng_data *__arm64_get_k_vdso_rnd_data(void) 33 - { 34 - return (void *)vdso_data + __VDSO_RND_DATA_OFFSET; 35 - } 36 - #define __arch_get_k_vdso_rng_data __arm64_get_k_vdso_rnd_data 37 - 38 - static __always_inline 39 - void __arm64_update_vsyscall(struct vdso_data *vdata) 16 + void __arm64_update_vsyscall(struct vdso_time_data *vdata) 40 17 { 41 18 vdata[CS_HRES_COARSE].mask = VDSO_PRECISION_MASK; 42 19 vdata[CS_RAW].mask = VDSO_PRECISION_MASK;
+5 -85
arch/arm64/kernel/vdso.c
··· 18 18 #include <linux/sched.h> 19 19 #include <linux/signal.h> 20 20 #include <linux/slab.h> 21 - #include <linux/time_namespace.h> 21 + #include <linux/vdso_datastore.h> 22 22 #include <linux/vmalloc.h> 23 23 #include <vdso/datapage.h> 24 24 #include <vdso/helpers.h> ··· 56 56 }, 57 57 #endif /* CONFIG_COMPAT_VDSO */ 58 58 }; 59 - 60 - /* 61 - * The vDSO data page. 62 - */ 63 - static union vdso_data_store vdso_data_store __page_aligned_data; 64 - struct vdso_data *vdso_data = vdso_data_store.data; 65 59 66 60 static int vdso_mremap(const struct vm_special_mapping *sm, 67 61 struct vm_area_struct *new_vma) ··· 98 104 return 0; 99 105 } 100 106 101 - #ifdef CONFIG_TIME_NS 102 - struct vdso_data *arch_get_vdso_data(void *vvar_page) 103 - { 104 - return (struct vdso_data *)(vvar_page); 105 - } 106 - 107 - static const struct vm_special_mapping vvar_map; 108 - 109 - /* 110 - * The vvar mapping contains data for a specific time namespace, so when a task 111 - * changes namespace we must unmap its vvar data for the old namespace. 112 - * Subsequent faults will map in data for the new namespace. 113 - * 114 - * For more details see timens_setup_vdso_data(). 115 - */ 116 - int vdso_join_timens(struct task_struct *task, struct time_namespace *ns) 117 - { 118 - struct mm_struct *mm = task->mm; 119 - struct vm_area_struct *vma; 120 - VMA_ITERATOR(vmi, mm, 0); 121 - 122 - mmap_read_lock(mm); 123 - 124 - for_each_vma(vmi, vma) { 125 - if (vma_is_special_mapping(vma, &vvar_map)) 126 - zap_vma_pages(vma); 127 - } 128 - 129 - mmap_read_unlock(mm); 130 - return 0; 131 - } 132 - #endif 133 - 134 - static vm_fault_t vvar_fault(const struct vm_special_mapping *sm, 135 - struct vm_area_struct *vma, struct vm_fault *vmf) 136 - { 137 - struct page *timens_page = find_timens_vvar_page(vma); 138 - unsigned long pfn; 139 - 140 - switch (vmf->pgoff) { 141 - case VVAR_DATA_PAGE_OFFSET: 142 - if (timens_page) 143 - pfn = page_to_pfn(timens_page); 144 - else 145 - pfn = sym_to_pfn(vdso_data); 146 - break; 147 - #ifdef CONFIG_TIME_NS 148 - case VVAR_TIMENS_PAGE_OFFSET: 149 - /* 150 - * If a task belongs to a time namespace then a namespace 151 - * specific VVAR is mapped with the VVAR_DATA_PAGE_OFFSET and 152 - * the real VVAR page is mapped with the VVAR_TIMENS_PAGE_OFFSET 153 - * offset. 154 - * See also the comment near timens_setup_vdso_data(). 155 - */ 156 - if (!timens_page) 157 - return VM_FAULT_SIGBUS; 158 - pfn = sym_to_pfn(vdso_data); 159 - break; 160 - #endif /* CONFIG_TIME_NS */ 161 - default: 162 - return VM_FAULT_SIGBUS; 163 - } 164 - 165 - return vmf_insert_pfn(vma, vmf->address, pfn); 166 - } 167 - 168 - static const struct vm_special_mapping vvar_map = { 169 - .name = "[vvar]", 170 - .fault = vvar_fault, 171 - }; 172 - 173 107 static int __setup_additional_pages(enum vdso_abi abi, 174 108 struct mm_struct *mm, 175 109 struct linux_binprm *bprm, ··· 107 185 unsigned long gp_flags = 0; 108 186 void *ret; 109 187 110 - BUILD_BUG_ON(VVAR_NR_PAGES != __VVAR_PAGES); 188 + BUILD_BUG_ON(VDSO_NR_PAGES != __VDSO_PAGES); 111 189 112 190 vdso_text_len = vdso_info[abi].vdso_pages << PAGE_SHIFT; 113 191 /* Be sure to map the data page */ 114 - vdso_mapping_len = vdso_text_len + VVAR_NR_PAGES * PAGE_SIZE; 192 + vdso_mapping_len = vdso_text_len + VDSO_NR_PAGES * PAGE_SIZE; 115 193 116 194 vdso_base = get_unmapped_area(NULL, 0, vdso_mapping_len, 0, 0); 117 195 if (IS_ERR_VALUE(vdso_base)) { ··· 119 197 goto up_fail; 120 198 } 121 199 122 - ret = _install_special_mapping(mm, vdso_base, VVAR_NR_PAGES * PAGE_SIZE, 123 - VM_READ|VM_MAYREAD|VM_PFNMAP, 124 - &vvar_map); 200 + ret = vdso_install_vvar_mapping(mm, vdso_base); 125 201 if (IS_ERR(ret)) 126 202 goto up_fail; 127 203 128 204 if (system_supports_bti_kernel()) 129 205 gp_flags = VM_ARM64_BTI; 130 206 131 - vdso_base += VVAR_NR_PAGES * PAGE_SIZE; 207 + vdso_base += VDSO_NR_PAGES * PAGE_SIZE; 132 208 mm->context.vdso = (void *)vdso_base; 133 209 ret = _install_special_mapping(mm, vdso_base, vdso_text_len, 134 210 VM_READ|VM_EXEC|gp_flags|
+2 -5
arch/arm64/kernel/vdso/vdso.lds.S
··· 20 20 21 21 SECTIONS 22 22 { 23 - PROVIDE(_vdso_data = . - __VVAR_PAGES * PAGE_SIZE); 24 - PROVIDE(_vdso_rng_data = _vdso_data + __VDSO_RND_DATA_OFFSET); 25 - #ifdef CONFIG_TIME_NS 26 - PROVIDE(_timens_data = _vdso_data + PAGE_SIZE); 27 - #endif 23 + VDSO_VVAR_SYMS 24 + 28 25 . = SIZEOF_HEADERS; 29 26 30 27 .hash : { *(.hash) } :text
+3 -4
arch/arm64/kernel/vdso32/vdso.lds.S
··· 12 12 #include <asm/page.h> 13 13 #include <asm/vdso.h> 14 14 #include <asm-generic/vmlinux.lds.h> 15 + #include <vdso/datapage.h> 15 16 16 17 OUTPUT_FORMAT("elf32-littlearm", "elf32-bigarm", "elf32-littlearm") 17 18 OUTPUT_ARCH(arm) 18 19 19 20 SECTIONS 20 21 { 21 - PROVIDE_HIDDEN(_vdso_data = . - __VVAR_PAGES * PAGE_SIZE); 22 - #ifdef CONFIG_TIME_NS 23 - PROVIDE_HIDDEN(_timens_data = _vdso_data + PAGE_SIZE); 24 - #endif 22 + VDSO_VVAR_SYMS 23 + 25 24 . = SIZEOF_HEADERS; 26 25 27 26 .hash : { *(.hash) } :text