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

ARM: 8930/1: Add support for generic vDSO

The arm vDSO library requires some adaptations to take advantage of
the newly introduced generic vDSO library.

Introduce the following changes:
- Modification vdso.c to be compliant with the common vdso datapage
- Use of lib/vdso for gettimeofday
- Implementation of elf note

Signed-off-by: Vincenzo Frascino <vincenzo.frascino@arm.com>
Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>

authored by

Vincenzo Frascino and committed by
Russell King
20e2fc42 9f1984c6

+195 -357
+74
arch/arm/include/asm/vdso/gettimeofday.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + /* 3 + * Copyright (C) 2018 ARM Limited 4 + */ 5 + #ifndef __ASM_VDSO_GETTIMEOFDAY_H 6 + #define __ASM_VDSO_GETTIMEOFDAY_H 7 + 8 + #ifndef __ASSEMBLY__ 9 + 10 + #include <asm/barrier.h> 11 + #include <asm/cp15.h> 12 + #include <asm/unistd.h> 13 + #include <uapi/linux/time.h> 14 + 15 + extern struct vdso_data *__get_datapage(void); 16 + 17 + static __always_inline int gettimeofday_fallback( 18 + struct __kernel_old_timeval *_tv, 19 + struct timezone *_tz) 20 + { 21 + register struct timezone *tz asm("r1") = _tz; 22 + register struct __kernel_old_timeval *tv asm("r0") = _tv; 23 + register long ret asm ("r0"); 24 + register long nr asm("r7") = __NR_gettimeofday; 25 + 26 + asm volatile( 27 + " swi #0\n" 28 + : "=r" (ret) 29 + : "r" (tv), "r" (tz), "r" (nr) 30 + : "memory"); 31 + 32 + return ret; 33 + } 34 + 35 + static __always_inline long clock_gettime_fallback( 36 + clockid_t _clkid, 37 + struct __kernel_timespec *_ts) 38 + { 39 + register struct __kernel_timespec *ts asm("r1") = _ts; 40 + register clockid_t clkid asm("r0") = _clkid; 41 + register long ret asm ("r0"); 42 + register long nr asm("r7") = __NR_clock_gettime64; 43 + 44 + asm volatile( 45 + " swi #0\n" 46 + : "=r" (ret) 47 + : "r" (clkid), "r" (ts), "r" (nr) 48 + : "memory"); 49 + 50 + return ret; 51 + } 52 + 53 + static __always_inline u64 __arch_get_hw_counter(int clock_mode) 54 + { 55 + #ifdef CONFIG_ARM_ARCH_TIMER 56 + u64 cycle_now; 57 + 58 + isb(); 59 + cycle_now = read_sysreg(CNTVCT); 60 + 61 + return cycle_now; 62 + #else 63 + return -EINVAL; /* use fallback */ 64 + #endif 65 + } 66 + 67 + static __always_inline const struct vdso_data *__arch_get_vdso_data(void) 68 + { 69 + return __get_datapage(); 70 + } 71 + 72 + #endif /* !__ASSEMBLY__ */ 73 + 74 + #endif /* __ASM_VDSO_GETTIMEOFDAY_H */
+71
arch/arm/include/asm/vdso/vsyscall.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + #ifndef __ASM_VDSO_VSYSCALL_H 3 + #define __ASM_VDSO_VSYSCALL_H 4 + 5 + #ifndef __ASSEMBLY__ 6 + 7 + #include <linux/timekeeper_internal.h> 8 + #include <vdso/datapage.h> 9 + #include <asm/cacheflush.h> 10 + 11 + extern struct vdso_data *vdso_data; 12 + extern bool cntvct_ok; 13 + 14 + static __always_inline 15 + bool tk_is_cntvct(const struct timekeeper *tk) 16 + { 17 + if (!IS_ENABLED(CONFIG_ARM_ARCH_TIMER)) 18 + return false; 19 + 20 + if (!tk->tkr_mono.clock->archdata.vdso_direct) 21 + return false; 22 + 23 + return true; 24 + } 25 + 26 + /* 27 + * Update the vDSO data page to keep in sync with kernel timekeeping. 28 + */ 29 + static __always_inline 30 + struct vdso_data *__arm_get_k_vdso_data(void) 31 + { 32 + return vdso_data; 33 + } 34 + #define __arch_get_k_vdso_data __arm_get_k_vdso_data 35 + 36 + static __always_inline 37 + int __arm_update_vdso_data(void) 38 + { 39 + return !cntvct_ok; 40 + } 41 + #define __arch_update_vdso_data __arm_update_vdso_data 42 + 43 + static __always_inline 44 + int __arm_get_clock_mode(struct timekeeper *tk) 45 + { 46 + u32 __tk_is_cntvct = tk_is_cntvct(tk); 47 + 48 + return __tk_is_cntvct; 49 + } 50 + #define __arch_get_clock_mode __arm_get_clock_mode 51 + 52 + static __always_inline 53 + int __arm_use_vsyscall(struct vdso_data *vdata) 54 + { 55 + return vdata[CS_HRES_COARSE].clock_mode; 56 + } 57 + #define __arch_use_vsyscall __arm_use_vsyscall 58 + 59 + static __always_inline 60 + void __arm_sync_vdso_data(struct vdso_data *vdata) 61 + { 62 + flush_dcache_page(virt_to_page(vdata)); 63 + } 64 + #define __arch_sync_vdso_data __arm_sync_vdso_data 65 + 66 + /* The asm-generic header needs to be included after the definitions above */ 67 + #include <asm-generic/vdso/vsyscall.h> 68 + 69 + #endif /* !__ASSEMBLY__ */ 70 + 71 + #endif /* __ASM_VDSO_VSYSCALL_H */
+3 -26
arch/arm/include/asm/vdso_datapage.h
··· 11 11 12 12 #ifndef __ASSEMBLY__ 13 13 14 + #include <vdso/datapage.h> 14 15 #include <asm/page.h> 15 16 16 - /* Try to be cache-friendly on systems that don't implement the 17 - * generic timer: fit the unconditionally updated fields in the first 18 - * 32 bytes. 19 - */ 20 - struct vdso_data { 21 - u32 seq_count; /* sequence count - odd during updates */ 22 - u16 tk_is_cntvct; /* fall back to syscall if false */ 23 - u16 cs_shift; /* clocksource shift */ 24 - u32 xtime_coarse_sec; /* coarse time */ 25 - u32 xtime_coarse_nsec; 26 - 27 - u32 wtm_clock_sec; /* wall to monotonic offset */ 28 - u32 wtm_clock_nsec; 29 - u32 xtime_clock_sec; /* CLOCK_REALTIME - seconds */ 30 - u32 cs_mult; /* clocksource multiplier */ 31 - 32 - u64 cs_cycle_last; /* last cycle value */ 33 - u64 cs_mask; /* clocksource mask */ 34 - 35 - u64 xtime_clock_snsec; /* CLOCK_REALTIME sub-ns base */ 36 - u32 tz_minuteswest; /* timezone info for gettimeofday(2) */ 37 - u32 tz_dsttime; 38 - }; 39 - 40 17 union vdso_data_store { 41 - struct vdso_data data; 42 - u8 page[PAGE_SIZE]; 18 + struct vdso_data data[CS_BASES]; 19 + u8 page[PAGE_SIZE]; 43 20 }; 44 21 45 22 #endif /* !__ASSEMBLY__ */
+4 -83
arch/arm/kernel/vdso.c
··· 23 23 #include <asm/vdso.h> 24 24 #include <asm/vdso_datapage.h> 25 25 #include <clocksource/arm_arch_timer.h> 26 + #include <vdso/helpers.h> 27 + #include <vdso/vsyscall.h> 26 28 27 29 #define MAX_SYMNAME 64 28 30 ··· 39 37 * The VDSO data page. 40 38 */ 41 39 static union vdso_data_store vdso_data_store __page_aligned_data; 42 - static struct vdso_data *vdso_data = &vdso_data_store.data; 40 + struct vdso_data *vdso_data = vdso_data_store.data; 43 41 44 42 static struct page *vdso_data_page __ro_after_init; 45 43 static const struct vm_special_mapping vdso_data_mapping = { ··· 79 77 /* Cached result of boot-time check for whether the arch timer exists, 80 78 * and if so, whether the virtual counter is useable. 81 79 */ 82 - static bool cntvct_ok __ro_after_init; 80 + bool cntvct_ok __ro_after_init; 83 81 84 82 static bool __init cntvct_functional(void) 85 83 { ··· 264 262 mm->context.vdso = addr; 265 263 } 266 264 267 - static void vdso_write_begin(struct vdso_data *vdata) 268 - { 269 - ++vdso_data->seq_count; 270 - smp_wmb(); /* Pairs with smp_rmb in vdso_read_retry */ 271 - } 272 - 273 - static void vdso_write_end(struct vdso_data *vdata) 274 - { 275 - smp_wmb(); /* Pairs with smp_rmb in vdso_read_begin */ 276 - ++vdso_data->seq_count; 277 - } 278 - 279 - static bool tk_is_cntvct(const struct timekeeper *tk) 280 - { 281 - if (!IS_ENABLED(CONFIG_ARM_ARCH_TIMER)) 282 - return false; 283 - 284 - if (!tk->tkr_mono.clock->archdata.vdso_direct) 285 - return false; 286 - 287 - return true; 288 - } 289 - 290 - /** 291 - * update_vsyscall - update the vdso data page 292 - * 293 - * Increment the sequence counter, making it odd, indicating to 294 - * userspace that an update is in progress. Update the fields used 295 - * for coarse clocks and, if the architected system timer is in use, 296 - * the fields used for high precision clocks. Increment the sequence 297 - * counter again, making it even, indicating to userspace that the 298 - * update is finished. 299 - * 300 - * Userspace is expected to sample seq_count before reading any other 301 - * fields from the data page. If seq_count is odd, userspace is 302 - * expected to wait until it becomes even. After copying data from 303 - * the page, userspace must sample seq_count again; if it has changed 304 - * from its previous value, userspace must retry the whole sequence. 305 - * 306 - * Calls to update_vsyscall are serialized by the timekeeping core. 307 - */ 308 - void update_vsyscall(struct timekeeper *tk) 309 - { 310 - struct timespec64 *wtm = &tk->wall_to_monotonic; 311 - 312 - if (!cntvct_ok) { 313 - /* The entry points have been zeroed, so there is no 314 - * point in updating the data page. 315 - */ 316 - return; 317 - } 318 - 319 - vdso_write_begin(vdso_data); 320 - 321 - vdso_data->tk_is_cntvct = tk_is_cntvct(tk); 322 - vdso_data->xtime_coarse_sec = tk->xtime_sec; 323 - vdso_data->xtime_coarse_nsec = (u32)(tk->tkr_mono.xtime_nsec >> 324 - tk->tkr_mono.shift); 325 - vdso_data->wtm_clock_sec = wtm->tv_sec; 326 - vdso_data->wtm_clock_nsec = wtm->tv_nsec; 327 - 328 - if (vdso_data->tk_is_cntvct) { 329 - vdso_data->cs_cycle_last = tk->tkr_mono.cycle_last; 330 - vdso_data->xtime_clock_sec = tk->xtime_sec; 331 - vdso_data->xtime_clock_snsec = tk->tkr_mono.xtime_nsec; 332 - vdso_data->cs_mult = tk->tkr_mono.mult; 333 - vdso_data->cs_shift = tk->tkr_mono.shift; 334 - vdso_data->cs_mask = tk->tkr_mono.mask; 335 - } 336 - 337 - vdso_write_end(vdso_data); 338 - 339 - flush_dcache_page(virt_to_page(vdso_data)); 340 - } 341 - 342 - void update_vsyscall_tz(void) 343 - { 344 - vdso_data->tz_minuteswest = sys_tz.tz_minuteswest; 345 - vdso_data->tz_dsttime = sys_tz.tz_dsttime; 346 - flush_dcache_page(virt_to_page(vdso_data)); 347 - }
+3
arch/arm/mm/Kconfig
··· 896 896 bool "Enable VDSO for acceleration of some system calls" 897 897 depends on AEABI && MMU && CPU_V7 898 898 default y if ARM_ARCH_TIMER 899 + select HAVE_GENERIC_VDSO 899 900 select GENERIC_TIME_VSYSCALL 901 + select GENERIC_VDSO_32 902 + select GENERIC_GETTIMEOFDAY 900 903 help 901 904 Place in the process address space an ELF shared object 902 905 providing fast implementations of gettimeofday and
+16 -2
arch/arm/vdso/Makefile
··· 1 1 # SPDX-License-Identifier: GPL-2.0 2 + 3 + # Absolute relocation type $(ARCH_REL_TYPE_ABS) needs to be defined before 4 + # the inclusion of generic Makefile. 5 + ARCH_REL_TYPE_ABS := R_ARM_JUMP_SLOT|R_ARM_GLOB_DAT|R_ARM_ABS32 6 + include $(srctree)/lib/vdso/Makefile 7 + 2 8 hostprogs-y := vdsomunge 3 9 4 - obj-vdso := vgettimeofday.o datapage.o 10 + obj-vdso := vgettimeofday.o datapage.o note.o 5 11 6 12 # Build rules 7 13 targets := $(obj-vdso) vdso.so vdso.so.dbg vdso.so.raw vdso.lds ··· 30 24 31 25 # Force -O2 to avoid libgcc dependencies 32 26 CFLAGS_REMOVE_vgettimeofday.o = -pg -Os 27 + ifeq ($(c-gettimeofday-y),) 33 28 CFLAGS_vgettimeofday.o = -O2 29 + else 30 + CFLAGS_vgettimeofday.o = -O2 -include $(c-gettimeofday-y) 31 + endif 34 32 35 33 # Disable gcov profiling for VDSO code 36 34 GCOV_PROFILE := n ··· 47 37 48 38 # Link rule for the .so file 49 39 $(obj)/vdso.so.raw: $(obj)/vdso.lds $(obj-vdso) FORCE 50 - $(call if_changed,ld) 40 + $(call if_changed,vdsold_and_vdso_check) 51 41 52 42 $(obj)/vdso.so.dbg: $(obj)/vdso.so.raw $(obj)/vdsomunge FORCE 53 43 $(call if_changed,vdsomunge) ··· 56 46 $(obj)/%.so: OBJCOPYFLAGS := -S 57 47 $(obj)/%.so: $(obj)/%.so.dbg FORCE 58 48 $(call if_changed,objcopy) 49 + 50 + # Actual build commands 51 + quiet_cmd_vdsold_and_vdso_check = LD $@ 52 + cmd_vdsold_and_vdso_check = $(cmd_ld); $(cmd_vdso_check) 59 53 60 54 quiet_cmd_vdsomunge = MUNGE $@ 61 55 cmd_vdsomunge = $(objtree)/$(obj)/vdsomunge $< $@
+15
arch/arm/vdso/note.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Copyright (C) 2012-2018 ARM Limited 4 + * 5 + * This supplies .note.* sections to go into the PT_NOTE inside the vDSO text. 6 + * Here we can supply some information useful to userland. 7 + */ 8 + 9 + #include <linux/uts.h> 10 + #include <linux/version.h> 11 + #include <linux/elfnote.h> 12 + #include <linux/build-salt.h> 13 + 14 + ELFNOTE32("Linux", 0, LINUX_VERSION_CODE); 15 + BUILD_SALT;
+9 -246
arch/arm/vdso/vgettimeofday.c
··· 1 1 // SPDX-License-Identifier: GPL-2.0-only 2 2 /* 3 + * ARM userspace implementations of gettimeofday() and similar. 4 + * 3 5 * Copyright 2015 Mentor Graphics Corporation. 4 6 */ 5 - 6 - #include <linux/compiler.h> 7 - #include <linux/hrtimer.h> 8 7 #include <linux/time.h> 9 - #include <asm/barrier.h> 10 - #include <asm/bug.h> 11 - #include <asm/cp15.h> 12 - #include <asm/page.h> 13 - #include <asm/unistd.h> 14 - #include <asm/vdso_datapage.h> 8 + #include <linux/types.h> 15 9 16 - #ifndef CONFIG_AEABI 17 - #error This code depends on AEABI system call conventions 18 - #endif 19 - 20 - extern struct vdso_data *__get_datapage(void); 21 - 22 - static notrace u32 __vdso_read_begin(const struct vdso_data *vdata) 10 + int __vdso_clock_gettime(clockid_t clock, 11 + struct old_timespec32 *ts) 23 12 { 24 - u32 seq; 25 - repeat: 26 - seq = READ_ONCE(vdata->seq_count); 27 - if (seq & 1) { 28 - cpu_relax(); 29 - goto repeat; 30 - } 31 - return seq; 13 + return __cvdso_clock_gettime32(clock, ts); 32 14 } 33 15 34 - static notrace u32 vdso_read_begin(const struct vdso_data *vdata) 16 + int __vdso_gettimeofday(struct __kernel_old_timeval *tv, 17 + struct timezone *tz) 35 18 { 36 - u32 seq; 37 - 38 - seq = __vdso_read_begin(vdata); 39 - 40 - smp_rmb(); /* Pairs with smp_wmb in vdso_write_end */ 41 - return seq; 42 - } 43 - 44 - static notrace int vdso_read_retry(const struct vdso_data *vdata, u32 start) 45 - { 46 - smp_rmb(); /* Pairs with smp_wmb in vdso_write_begin */ 47 - return vdata->seq_count != start; 48 - } 49 - 50 - static notrace long clock_gettime_fallback(clockid_t _clkid, 51 - struct timespec *_ts) 52 - { 53 - register struct timespec *ts asm("r1") = _ts; 54 - register clockid_t clkid asm("r0") = _clkid; 55 - register long ret asm ("r0"); 56 - register long nr asm("r7") = __NR_clock_gettime; 57 - 58 - asm volatile( 59 - " swi #0\n" 60 - : "=r" (ret) 61 - : "r" (clkid), "r" (ts), "r" (nr) 62 - : "memory"); 63 - 64 - return ret; 65 - } 66 - 67 - static notrace int do_realtime_coarse(struct timespec *ts, 68 - struct vdso_data *vdata) 69 - { 70 - u32 seq; 71 - 72 - do { 73 - seq = vdso_read_begin(vdata); 74 - 75 - ts->tv_sec = vdata->xtime_coarse_sec; 76 - ts->tv_nsec = vdata->xtime_coarse_nsec; 77 - 78 - } while (vdso_read_retry(vdata, seq)); 79 - 80 - return 0; 81 - } 82 - 83 - static notrace int do_monotonic_coarse(struct timespec *ts, 84 - struct vdso_data *vdata) 85 - { 86 - struct timespec tomono; 87 - u32 seq; 88 - 89 - do { 90 - seq = vdso_read_begin(vdata); 91 - 92 - ts->tv_sec = vdata->xtime_coarse_sec; 93 - ts->tv_nsec = vdata->xtime_coarse_nsec; 94 - 95 - tomono.tv_sec = vdata->wtm_clock_sec; 96 - tomono.tv_nsec = vdata->wtm_clock_nsec; 97 - 98 - } while (vdso_read_retry(vdata, seq)); 99 - 100 - ts->tv_sec += tomono.tv_sec; 101 - timespec_add_ns(ts, tomono.tv_nsec); 102 - 103 - return 0; 104 - } 105 - 106 - #ifdef CONFIG_ARM_ARCH_TIMER 107 - 108 - static notrace u64 get_ns(struct vdso_data *vdata) 109 - { 110 - u64 cycle_delta; 111 - u64 cycle_now; 112 - u64 nsec; 113 - 114 - isb(); 115 - cycle_now = read_sysreg(CNTVCT); 116 - 117 - cycle_delta = (cycle_now - vdata->cs_cycle_last) & vdata->cs_mask; 118 - 119 - nsec = (cycle_delta * vdata->cs_mult) + vdata->xtime_clock_snsec; 120 - nsec >>= vdata->cs_shift; 121 - 122 - return nsec; 123 - } 124 - 125 - static notrace int do_realtime(struct timespec *ts, struct vdso_data *vdata) 126 - { 127 - u64 nsecs; 128 - u32 seq; 129 - 130 - do { 131 - seq = vdso_read_begin(vdata); 132 - 133 - if (!vdata->tk_is_cntvct) 134 - return -1; 135 - 136 - ts->tv_sec = vdata->xtime_clock_sec; 137 - nsecs = get_ns(vdata); 138 - 139 - } while (vdso_read_retry(vdata, seq)); 140 - 141 - ts->tv_nsec = 0; 142 - timespec_add_ns(ts, nsecs); 143 - 144 - return 0; 145 - } 146 - 147 - static notrace int do_monotonic(struct timespec *ts, struct vdso_data *vdata) 148 - { 149 - struct timespec tomono; 150 - u64 nsecs; 151 - u32 seq; 152 - 153 - do { 154 - seq = vdso_read_begin(vdata); 155 - 156 - if (!vdata->tk_is_cntvct) 157 - return -1; 158 - 159 - ts->tv_sec = vdata->xtime_clock_sec; 160 - nsecs = get_ns(vdata); 161 - 162 - tomono.tv_sec = vdata->wtm_clock_sec; 163 - tomono.tv_nsec = vdata->wtm_clock_nsec; 164 - 165 - } while (vdso_read_retry(vdata, seq)); 166 - 167 - ts->tv_sec += tomono.tv_sec; 168 - ts->tv_nsec = 0; 169 - timespec_add_ns(ts, nsecs + tomono.tv_nsec); 170 - 171 - return 0; 172 - } 173 - 174 - #else /* CONFIG_ARM_ARCH_TIMER */ 175 - 176 - static notrace int do_realtime(struct timespec *ts, struct vdso_data *vdata) 177 - { 178 - return -1; 179 - } 180 - 181 - static notrace int do_monotonic(struct timespec *ts, struct vdso_data *vdata) 182 - { 183 - return -1; 184 - } 185 - 186 - #endif /* CONFIG_ARM_ARCH_TIMER */ 187 - 188 - notrace int __vdso_clock_gettime(clockid_t clkid, struct timespec *ts) 189 - { 190 - struct vdso_data *vdata; 191 - int ret = -1; 192 - 193 - vdata = __get_datapage(); 194 - 195 - switch (clkid) { 196 - case CLOCK_REALTIME_COARSE: 197 - ret = do_realtime_coarse(ts, vdata); 198 - break; 199 - case CLOCK_MONOTONIC_COARSE: 200 - ret = do_monotonic_coarse(ts, vdata); 201 - break; 202 - case CLOCK_REALTIME: 203 - ret = do_realtime(ts, vdata); 204 - break; 205 - case CLOCK_MONOTONIC: 206 - ret = do_monotonic(ts, vdata); 207 - break; 208 - default: 209 - break; 210 - } 211 - 212 - if (ret) 213 - ret = clock_gettime_fallback(clkid, ts); 214 - 215 - return ret; 216 - } 217 - 218 - static notrace long gettimeofday_fallback(struct timeval *_tv, 219 - struct timezone *_tz) 220 - { 221 - register struct timezone *tz asm("r1") = _tz; 222 - register struct timeval *tv asm("r0") = _tv; 223 - register long ret asm ("r0"); 224 - register long nr asm("r7") = __NR_gettimeofday; 225 - 226 - asm volatile( 227 - " swi #0\n" 228 - : "=r" (ret) 229 - : "r" (tv), "r" (tz), "r" (nr) 230 - : "memory"); 231 - 232 - return ret; 233 - } 234 - 235 - notrace int __vdso_gettimeofday(struct timeval *tv, struct timezone *tz) 236 - { 237 - struct timespec ts; 238 - struct vdso_data *vdata; 239 - int ret; 240 - 241 - vdata = __get_datapage(); 242 - 243 - ret = do_realtime(&ts, vdata); 244 - if (ret) 245 - return gettimeofday_fallback(tv, tz); 246 - 247 - if (tv) { 248 - tv->tv_sec = ts.tv_sec; 249 - tv->tv_usec = ts.tv_nsec / 1000; 250 - } 251 - if (tz) { 252 - tz->tz_minuteswest = vdata->tz_minuteswest; 253 - tz->tz_dsttime = vdata->tz_dsttime; 254 - } 255 - 256 - return ret; 19 + return __cvdso_gettimeofday(tv, tz); 257 20 } 258 21 259 22 /* Avoid unresolved references emitted by GCC */