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

vdso: Rework struct vdso_time_data and introduce struct vdso_clock

To support multiple PTP clocks, the VDSO data structure needs to be
reworked. All clock specific data will end up in struct vdso_clock and in
struct vdso_time_data there will be an array of VDSO clocks.

Now that all preparatory changes are in place:

Split the clock related struct members into a separate struct
vdso_clock. Make sure all users are aware, that vdso_time_data is no longer
initialized as an array and vdso_clock is now the array inside
vdso_data. Remove the vdso_clock define, which mapped it to vdso_time_data
for the transition.

Signed-off-by: Anna-Maria Behnsen <anna-maria@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/20250303-vdso-clock-v1-19-c1b5c69a166f@linutronix.de

authored by

Anna-Maria Behnsen and committed by
Thomas Gleixner
886653e3 97a5a90c

+57 -54
+1 -1
arch/arm64/include/asm/vdso/compat_gettimeofday.h
··· 149 149 * where __aarch64_get_vdso_u_time_data() is called, and then keep the 150 150 * result in a register. 151 151 */ 152 - asm volatile("mov %0, %1" : "=r"(ret) : "r"(vdso_u_time_data)); 152 + asm volatile("mov %0, %1" : "=r"(ret) : "r"(&vdso_u_time_data)); 153 153 154 154 return ret; 155 155 }
+2 -2
arch/arm64/include/asm/vdso/vsyscall.h
··· 15 15 static __always_inline 16 16 void __arm64_update_vsyscall(struct vdso_time_data *vdata) 17 17 { 18 - vdata[CS_HRES_COARSE].mask = VDSO_PRECISION_MASK; 19 - vdata[CS_RAW].mask = VDSO_PRECISION_MASK; 18 + vdata->clock_data[CS_HRES_COARSE].mask = VDSO_PRECISION_MASK; 19 + vdata->clock_data[CS_RAW].mask = VDSO_PRECISION_MASK; 20 20 } 21 21 #define __arch_update_vsyscall __arm64_update_vsyscall 22 22
+3 -8
arch/s390/kernel/time.c
··· 79 79 { 80 80 struct ptff_qto qto; 81 81 struct ptff_qui qui; 82 - int cs; 83 82 84 83 /* Initialize TOD steering parameters */ 85 84 tod_steering_end = tod_clock_base.tod; 86 - for (cs = 0; cs < CS_BASES; cs++) 87 - vdso_k_time_data[cs].arch_data.tod_steering_end = tod_steering_end; 85 + vdso_k_time_data->arch_data.tod_steering_end = tod_steering_end; 88 86 89 87 if (!test_facility(28)) 90 88 return; ··· 371 373 { 372 374 unsigned long now, adj; 373 375 struct ptff_qto qto; 374 - int cs; 375 376 376 377 /* Fixup the monotonic sched clock. */ 377 378 tod_clock_base.eitod += delta; ··· 386 389 panic("TOD clock sync offset %li is too large to drift\n", 387 390 tod_steering_delta); 388 391 tod_steering_end = now + (abs(tod_steering_delta) << 15); 389 - for (cs = 0; cs < CS_BASES; cs++) { 390 - vdso_k_time_data[cs].arch_data.tod_steering_end = tod_steering_end; 391 - vdso_k_time_data[cs].arch_data.tod_steering_delta = tod_steering_delta; 392 - } 392 + vdso_k_time_data->arch_data.tod_steering_end = tod_steering_end; 393 + vdso_k_time_data->arch_data.tod_steering_delta = tod_steering_delta; 393 394 394 395 /* Update LPAR offset. */ 395 396 if (ptff_query(PTFF_QTO) && ptff(&qto, sizeof(qto), PTFF_QTO) == 0)
+1 -1
include/asm-generic/vdso/vsyscall.h
··· 9 9 #ifndef __arch_get_vdso_u_time_data 10 10 static __always_inline const struct vdso_time_data *__arch_get_vdso_u_time_data(void) 11 11 { 12 - return vdso_u_time_data; 12 + return &vdso_u_time_data; 13 13 } 14 14 #endif 15 15
+32 -23
include/vdso/datapage.h
··· 69 69 }; 70 70 71 71 /** 72 - * struct vdso_time_data - vdso datapage representation 73 - * @arch_data: architecture specific data (optional, defaults 74 - * to an empty struct) 72 + * struct vdso_clock - vdso per clocksource datapage representation 75 73 * @seq: timebase sequence counter 76 74 * @clock_mode: clock mode 77 75 * @cycle_last: timebase at clocksource init ··· 79 81 * @shift: clocksource shift 80 82 * @basetime[clock_id]: basetime per clock_id 81 83 * @offset[clock_id]: time namespace offset per clock_id 82 - * @tz_minuteswest: minutes west of Greenwich 83 - * @tz_dsttime: type of DST correction 84 - * @hrtimer_res: hrtimer resolution 85 - * @__unused: unused 86 84 * 87 - * vdso_time_data will be accessed by 64 bit and compat code at the same time 88 - * so we should be careful before modifying this structure. 89 - * 90 - * The ordering of the struct members is optimized to have fast access to the 91 - * often required struct members which are related to CLOCK_REALTIME and 92 - * CLOCK_MONOTONIC. This information is stored in the first cache lines. 85 + * See also struct vdso_time_data for basic access and ordering information as 86 + * struct vdso_clock is used there. 93 87 * 94 88 * @basetime is used to store the base time for the system wide time getter 95 89 * VVAR page. ··· 94 104 * For clocks which are not affected by time namespace adjustment the 95 105 * offset must be zero. 96 106 */ 97 - struct vdso_time_data { 98 - struct arch_vdso_time_data arch_data; 99 - 107 + struct vdso_clock { 100 108 u32 seq; 101 109 102 110 s32 clock_mode; ··· 110 122 struct vdso_timestamp basetime[VDSO_BASES]; 111 123 struct timens_offset offset[VDSO_BASES]; 112 124 }; 125 + }; 113 126 114 - s32 tz_minuteswest; 115 - s32 tz_dsttime; 116 - u32 hrtimer_res; 117 - u32 __unused; 127 + /** 128 + * struct vdso_time_data - vdso datapage representation 129 + * @arch_data: architecture specific data (optional, defaults 130 + * to an empty struct) 131 + * @clock_data: clocksource related data (array) 132 + * @tz_minuteswest: minutes west of Greenwich 133 + * @tz_dsttime: type of DST correction 134 + * @hrtimer_res: hrtimer resolution 135 + * @__unused: unused 136 + * 137 + * vdso_time_data will be accessed by 64 bit and compat code at the same time 138 + * so we should be careful before modifying this structure. 139 + * 140 + * The ordering of the struct members is optimized to have fast acces to the 141 + * often required struct members which are related to CLOCK_REALTIME and 142 + * CLOCK_MONOTONIC. This information is stored in the first cache lines. 143 + */ 144 + struct vdso_time_data { 145 + struct arch_vdso_time_data arch_data; 146 + 147 + struct vdso_clock clock_data[CS_BASES]; 148 + 149 + s32 tz_minuteswest; 150 + s32 tz_dsttime; 151 + u32 hrtimer_res; 152 + u32 __unused; 118 153 } ____cacheline_aligned; 119 - 120 - #define vdso_clock vdso_time_data 121 154 122 155 /** 123 156 * struct vdso_rng_data - vdso RNG state information ··· 160 151 * relocation, and this is what we need. 161 152 */ 162 153 #ifdef CONFIG_GENERIC_VDSO_DATA_STORE 163 - extern struct vdso_time_data vdso_u_time_data[CS_BASES] __attribute__((visibility("hidden"))); 154 + extern struct vdso_time_data vdso_u_time_data __attribute__((visibility("hidden"))); 164 155 extern struct vdso_rng_data vdso_u_rng_data __attribute__((visibility("hidden"))); 165 156 extern struct vdso_arch_data vdso_u_arch_data __attribute__((visibility("hidden"))); 166 157
+2 -2
include/vdso/helpers.h
··· 30 30 31 31 static __always_inline void vdso_write_begin(struct vdso_time_data *vd) 32 32 { 33 - struct vdso_clock *vc = vd; 33 + struct vdso_clock *vc = vd->clock_data; 34 34 35 35 /* 36 36 * WRITE_ONCE() is required otherwise the compiler can validly tear ··· 44 44 45 45 static __always_inline void vdso_write_end(struct vdso_time_data *vd) 46 46 { 47 - struct vdso_clock *vc = vd; 47 + struct vdso_clock *vc = vd->clock_data; 48 48 49 49 smp_wmb(); 50 50 /*
+1 -1
kernel/time/namespace.c
··· 237 237 238 238 ns->frozen_offsets = true; 239 239 vdata = page_address(ns->vvar_page); 240 - vc = vdata; 240 + vc = vdata->clock_data; 241 241 242 242 for (i = 0; i < CS_BASES; i++) 243 243 timens_setup_vdso_clock_data(&vc[i], ns);
+5 -6
kernel/time/vsyscall.c
··· 17 17 18 18 static inline void update_vdso_time_data(struct vdso_time_data *vdata, struct timekeeper *tk) 19 19 { 20 + struct vdso_clock *vc = vdata->clock_data; 20 21 struct vdso_timestamp *vdso_ts; 21 - struct vdso_clock *vc = vdata; 22 22 u64 nsec, sec; 23 23 24 24 vc[CS_HRES_COARSE].cycle_last = tk->tkr_mono.cycle_last; ··· 78 78 void update_vsyscall(struct timekeeper *tk) 79 79 { 80 80 struct vdso_time_data *vdata = vdso_k_time_data; 81 + struct vdso_clock *vc = vdata->clock_data; 81 82 struct vdso_timestamp *vdso_ts; 82 - struct vdso_clock *vc = vdata; 83 83 s32 clock_mode; 84 84 u64 nsec; 85 85 ··· 109 109 110 110 /* 111 111 * Read without the seqlock held by clock_getres(). 112 - * Note: No need to have a second copy. 113 112 */ 114 - WRITE_ONCE(vdata[CS_HRES_COARSE].hrtimer_res, hrtimer_resolution); 113 + WRITE_ONCE(vdata->hrtimer_res, hrtimer_resolution); 115 114 116 115 /* 117 116 * If the current clocksource is not VDSO capable, then spare the ··· 130 131 { 131 132 struct vdso_time_data *vdata = vdso_k_time_data; 132 133 133 - vdata[CS_HRES_COARSE].tz_minuteswest = sys_tz.tz_minuteswest; 134 - vdata[CS_HRES_COARSE].tz_dsttime = sys_tz.tz_dsttime; 134 + vdata->tz_minuteswest = sys_tz.tz_minuteswest; 135 + vdata->tz_dsttime = sys_tz.tz_dsttime; 135 136 136 137 __arch_sync_vdso_time_data(vdata); 137 138 }
+2 -2
lib/vdso/datastore.c
··· 13 13 */ 14 14 #ifdef CONFIG_HAVE_GENERIC_VDSO 15 15 static union { 16 - struct vdso_time_data data[CS_BASES]; 16 + struct vdso_time_data data; 17 17 u8 page[PAGE_SIZE]; 18 18 } vdso_time_data_store __page_aligned_data; 19 - struct vdso_time_data *vdso_k_time_data = vdso_time_data_store.data; 19 + struct vdso_time_data *vdso_k_time_data = &vdso_time_data_store.data; 20 20 static_assert(sizeof(vdso_time_data_store) == PAGE_SIZE); 21 21 #endif /* CONFIG_HAVE_GENERIC_VDSO */ 22 22
+8 -8
lib/vdso/gettimeofday.c
··· 87 87 { 88 88 const struct vdso_time_data *vd = __arch_get_vdso_u_timens_data(vdns); 89 89 const struct timens_offset *offs = &vcns->offset[clk]; 90 + const struct vdso_clock *vc = vd->clock_data; 90 91 const struct vdso_timestamp *vdso_ts; 91 - const struct vdso_clock *vc = vd; 92 92 u64 cycles, ns; 93 93 u32 seq; 94 94 s64 sec; ··· 199 199 { 200 200 const struct vdso_time_data *vd = __arch_get_vdso_u_timens_data(vdns); 201 201 const struct timens_offset *offs = &vcns->offset[clk]; 202 + const struct vdso_clock *vc = vd->clock_data; 202 203 const struct vdso_timestamp *vdso_ts; 203 - const struct vdso_clock *vc = vd; 204 204 u64 nsec; 205 205 s64 sec; 206 206 s32 seq; ··· 265 265 __cvdso_clock_gettime_common(const struct vdso_time_data *vd, clockid_t clock, 266 266 struct __kernel_timespec *ts) 267 267 { 268 - const struct vdso_clock *vc = vd; 268 + const struct vdso_clock *vc = vd->clock_data; 269 269 u32 msk; 270 270 271 271 /* Check for negative values or invalid clocks */ ··· 337 337 __cvdso_gettimeofday_data(const struct vdso_time_data *vd, 338 338 struct __kernel_old_timeval *tv, struct timezone *tz) 339 339 { 340 - const struct vdso_clock *vc = vd; 340 + const struct vdso_clock *vc = vd->clock_data; 341 341 342 342 if (likely(tv != NULL)) { 343 343 struct __kernel_timespec ts; ··· 371 371 static __maybe_unused __kernel_old_time_t 372 372 __cvdso_time_data(const struct vdso_time_data *vd, __kernel_old_time_t *time) 373 373 { 374 - const struct vdso_clock *vc = vd; 374 + const struct vdso_clock *vc = vd->clock_data; 375 375 __kernel_old_time_t t; 376 376 377 377 if (IS_ENABLED(CONFIG_TIME_NS) && 378 378 vc->clock_mode == VDSO_CLOCKMODE_TIMENS) { 379 379 vd = __arch_get_vdso_u_timens_data(vd); 380 - vc = vd; 380 + vc = vd->clock_data; 381 381 } 382 382 383 383 t = READ_ONCE(vc[CS_HRES_COARSE].basetime[CLOCK_REALTIME].sec); ··· 399 399 int __cvdso_clock_getres_common(const struct vdso_time_data *vd, clockid_t clock, 400 400 struct __kernel_timespec *res) 401 401 { 402 - const struct vdso_clock *vc = vd; 402 + const struct vdso_clock *vc = vd->clock_data; 403 403 u32 msk; 404 404 u64 ns; 405 405 ··· 420 420 /* 421 421 * Preserves the behaviour of posix_get_hrtimer_res(). 422 422 */ 423 - ns = READ_ONCE(vd[CS_HRES_COARSE].hrtimer_res); 423 + ns = READ_ONCE(vd->hrtimer_res); 424 424 } else if (msk & VDSO_COARSE) { 425 425 /* 426 426 * Preserves the behaviour of posix_get_coarse_res().