@@ -45,21 +45,10 @@ notrace static long vdso_fallback_gettime(long clock, struct timespec *ts)
45
45
long ret ;
46
46
asm ("syscall" : "=a" (ret ), "=m" (* ts ) :
47
47
"0" (__NR_clock_gettime ), "D" (clock ), "S" (ts ) :
48
- "memory" , " rcx" , "r11" );
48
+ "rcx" , "r11" );
49
49
return ret ;
50
50
}
51
51
52
- notrace static long vdso_fallback_gtod (struct timeval * tv , struct timezone * tz )
53
- {
54
- long ret ;
55
-
56
- asm ("syscall" : "=a" (ret ), "=m" (* tv ), "=m" (* tz ) :
57
- "0" (__NR_gettimeofday ), "D" (tv ), "S" (tz ) :
58
- "memory" , "rcx" , "r11" );
59
- return ret ;
60
- }
61
-
62
-
63
52
#else
64
53
65
54
notrace static long vdso_fallback_gettime (long clock , struct timespec * ts )
@@ -73,22 +62,7 @@ notrace static long vdso_fallback_gettime(long clock, struct timespec *ts)
73
62
"mov %%edx, %%ebx \n"
74
63
: "=a" (ret ), "=m" (* ts )
75
64
: "0" (__NR_clock_gettime ), [clock ] "g" (clock ), "c" (ts )
76
- : "memory" , "edx" );
77
- return ret ;
78
- }
79
-
80
- notrace static long vdso_fallback_gtod (struct timeval * tv , struct timezone * tz )
81
- {
82
- long ret ;
83
-
84
- asm (
85
- "mov %%ebx, %%edx \n"
86
- "mov %[tv], %%ebx \n"
87
- "call __kernel_vsyscall \n"
88
- "mov %%edx, %%ebx \n"
89
- : "=a" (ret ), "=m" (* tv ), "=m" (* tz )
90
- : "0" (__NR_gettimeofday ), [tv ] "g" (tv ), "c" (tz )
91
- : "memory" , "edx" );
65
+ : "edx" );
92
66
return ret ;
93
67
}
94
68
@@ -100,12 +74,11 @@ static notrace const struct pvclock_vsyscall_time_info *get_pvti0(void)
100
74
return (const struct pvclock_vsyscall_time_info * )& pvclock_page ;
101
75
}
102
76
103
- static notrace u64 vread_pvclock (int * mode )
77
+ static notrace u64 vread_pvclock (void )
104
78
{
105
79
const struct pvclock_vcpu_time_info * pvti = & get_pvti0 ()-> pvti ;
106
- u64 ret ;
107
- u64 last ;
108
80
u32 version ;
81
+ u64 ret ;
109
82
110
83
/*
111
84
* Note: The kernel and hypervisor must guarantee that cpu ID
@@ -132,175 +105,112 @@ static notrace u64 vread_pvclock(int *mode)
132
105
do {
133
106
version = pvclock_read_begin (pvti );
134
107
135
- if (unlikely (!(pvti -> flags & PVCLOCK_TSC_STABLE_BIT ))) {
136
- * mode = VCLOCK_NONE ;
137
- return 0 ;
138
- }
108
+ if (unlikely (!(pvti -> flags & PVCLOCK_TSC_STABLE_BIT )))
109
+ return U64_MAX ;
139
110
140
111
ret = __pvclock_read_cycles (pvti , rdtsc_ordered ());
141
112
} while (pvclock_read_retry (pvti , version ));
142
113
143
- /* refer to vread_tsc() comment for rationale */
144
- last = gtod -> cycle_last ;
145
-
146
- if (likely (ret >= last ))
147
- return ret ;
148
-
149
- return last ;
114
+ return ret ;
150
115
}
151
116
#endif
152
117
#ifdef CONFIG_HYPERV_TSCPAGE
153
- static notrace u64 vread_hvclock (int * mode )
118
+ static notrace u64 vread_hvclock (void )
154
119
{
155
120
const struct ms_hyperv_tsc_page * tsc_pg =
156
121
(const struct ms_hyperv_tsc_page * )& hvclock_page ;
157
- u64 current_tick = hv_read_tsc_page (tsc_pg );
158
-
159
- if (current_tick != U64_MAX )
160
- return current_tick ;
161
122
162
- * mode = VCLOCK_NONE ;
163
- return 0 ;
123
+ return hv_read_tsc_page (tsc_pg );
164
124
}
165
125
#endif
166
126
167
- notrace static u64 vread_tsc ( void )
127
+ notrace static inline u64 vgetcyc ( int mode )
168
128
{
169
- u64 ret = (u64 )rdtsc_ordered ();
170
- u64 last = gtod -> cycle_last ;
171
-
172
- if (likely (ret >= last ))
173
- return ret ;
174
-
175
- /*
176
- * GCC likes to generate cmov here, but this branch is extremely
177
- * predictable (it's just a function of time and the likely is
178
- * very likely) and there's a data dependence, so force GCC
179
- * to generate a branch instead. I don't barrier() because
180
- * we don't actually need a barrier, and if this function
181
- * ever gets inlined it will generate worse code.
182
- */
183
- asm volatile ("" );
184
- return last ;
185
- }
186
-
187
- notrace static inline u64 vgetsns (int * mode )
188
- {
189
- u64 v ;
190
- cycles_t cycles ;
191
-
192
- if (gtod -> vclock_mode == VCLOCK_TSC )
193
- cycles = vread_tsc ();
129
+ if (mode == VCLOCK_TSC )
130
+ return (u64 )rdtsc_ordered ();
194
131
#ifdef CONFIG_PARAVIRT_CLOCK
195
- else if (gtod -> vclock_mode == VCLOCK_PVCLOCK )
196
- cycles = vread_pvclock (mode );
132
+ else if (mode == VCLOCK_PVCLOCK )
133
+ return vread_pvclock ();
197
134
#endif
198
135
#ifdef CONFIG_HYPERV_TSCPAGE
199
- else if (gtod -> vclock_mode == VCLOCK_HVCLOCK )
200
- cycles = vread_hvclock (mode );
136
+ else if (mode == VCLOCK_HVCLOCK )
137
+ return vread_hvclock ();
201
138
#endif
202
- else
203
- return 0 ;
204
- v = (cycles - gtod -> cycle_last ) & gtod -> mask ;
205
- return v * gtod -> mult ;
139
+ return U64_MAX ;
206
140
}
207
141
208
- /* Code size doesn't matter (vdso is 4k anyway) and this is faster. */
209
- notrace static int __always_inline do_realtime (struct timespec * ts )
142
+ notrace static int do_hres (clockid_t clk , struct timespec * ts )
210
143
{
211
- unsigned long seq ;
212
- u64 ns ;
213
- int mode ;
144
+ struct vgtod_ts * base = & gtod -> basetime [ clk ] ;
145
+ u64 cycles , last , sec , ns ;
146
+ unsigned int seq ;
214
147
215
148
do {
216
149
seq = gtod_read_begin (gtod );
217
- mode = gtod -> vclock_mode ;
218
- ts -> tv_sec = gtod -> wall_time_sec ;
219
- ns = gtod -> wall_time_snsec ;
220
- ns += vgetsns (& mode );
150
+ cycles = vgetcyc (gtod -> vclock_mode );
151
+ ns = base -> nsec ;
152
+ last = gtod -> cycle_last ;
153
+ if (unlikely ((s64 )cycles < 0 ))
154
+ return vdso_fallback_gettime (clk , ts );
155
+ if (cycles > last )
156
+ ns += (cycles - last ) * gtod -> mult ;
221
157
ns >>= gtod -> shift ;
158
+ sec = base -> sec ;
222
159
} while (unlikely (gtod_read_retry (gtod , seq )));
223
160
224
- ts -> tv_sec += __iter_div_u64_rem (ns , NSEC_PER_SEC , & ns );
161
+ /*
162
+ * Do this outside the loop: a race inside the loop could result
163
+ * in __iter_div_u64_rem() being extremely slow.
164
+ */
165
+ ts -> tv_sec = sec + __iter_div_u64_rem (ns , NSEC_PER_SEC , & ns );
225
166
ts -> tv_nsec = ns ;
226
167
227
- return mode ;
168
+ return 0 ;
228
169
}
229
170
230
- notrace static int __always_inline do_monotonic ( struct timespec * ts )
171
+ notrace static void do_coarse ( clockid_t clk , struct timespec * ts )
231
172
{
232
- unsigned long seq ;
233
- u64 ns ;
234
- int mode ;
173
+ struct vgtod_ts * base = & gtod -> basetime [clk ];
174
+ unsigned int seq ;
235
175
236
176
do {
237
177
seq = gtod_read_begin (gtod );
238
- mode = gtod -> vclock_mode ;
239
- ts -> tv_sec = gtod -> monotonic_time_sec ;
240
- ns = gtod -> monotonic_time_snsec ;
241
- ns += vgetsns (& mode );
242
- ns >>= gtod -> shift ;
178
+ ts -> tv_sec = base -> sec ;
179
+ ts -> tv_nsec = base -> nsec ;
243
180
} while (unlikely (gtod_read_retry (gtod , seq )));
244
-
245
- ts -> tv_sec += __iter_div_u64_rem (ns , NSEC_PER_SEC , & ns );
246
- ts -> tv_nsec = ns ;
247
-
248
- return mode ;
249
181
}
250
182
251
- notrace static void do_realtime_coarse ( struct timespec * ts )
183
+ notrace int __vdso_clock_gettime ( clockid_t clock , struct timespec * ts )
252
184
{
253
- unsigned long seq ;
254
- do {
255
- seq = gtod_read_begin (gtod );
256
- ts -> tv_sec = gtod -> wall_time_coarse_sec ;
257
- ts -> tv_nsec = gtod -> wall_time_coarse_nsec ;
258
- } while (unlikely (gtod_read_retry (gtod , seq )));
259
- }
185
+ unsigned int msk ;
260
186
261
- notrace static void do_monotonic_coarse (struct timespec * ts )
262
- {
263
- unsigned long seq ;
264
- do {
265
- seq = gtod_read_begin (gtod );
266
- ts -> tv_sec = gtod -> monotonic_time_coarse_sec ;
267
- ts -> tv_nsec = gtod -> monotonic_time_coarse_nsec ;
268
- } while (unlikely (gtod_read_retry (gtod , seq )));
269
- }
187
+ /* Sort out negative (CPU/FD) and invalid clocks */
188
+ if (unlikely ((unsigned int ) clock >= MAX_CLOCKS ))
189
+ return vdso_fallback_gettime (clock , ts );
270
190
271
- notrace int __vdso_clock_gettime (clockid_t clock , struct timespec * ts )
272
- {
273
- switch (clock ) {
274
- case CLOCK_REALTIME :
275
- if (do_realtime (ts ) == VCLOCK_NONE )
276
- goto fallback ;
277
- break ;
278
- case CLOCK_MONOTONIC :
279
- if (do_monotonic (ts ) == VCLOCK_NONE )
280
- goto fallback ;
281
- break ;
282
- case CLOCK_REALTIME_COARSE :
283
- do_realtime_coarse (ts );
284
- break ;
285
- case CLOCK_MONOTONIC_COARSE :
286
- do_monotonic_coarse (ts );
287
- break ;
288
- default :
289
- goto fallback ;
191
+ /*
192
+ * Convert the clockid to a bitmask and use it to check which
193
+ * clocks are handled in the VDSO directly.
194
+ */
195
+ msk = 1U << clock ;
196
+ if (likely (msk & VGTOD_HRES )) {
197
+ return do_hres (clock , ts );
198
+ } else if (msk & VGTOD_COARSE ) {
199
+ do_coarse (clock , ts );
200
+ return 0 ;
290
201
}
291
-
292
- return 0 ;
293
- fallback :
294
202
return vdso_fallback_gettime (clock , ts );
295
203
}
204
+
296
205
int clock_gettime (clockid_t , struct timespec * )
297
206
__attribute__((weak , alias ("__vdso_clock_gettime" )));
298
207
299
208
notrace int __vdso_gettimeofday (struct timeval * tv , struct timezone * tz )
300
209
{
301
210
if (likely (tv != NULL )) {
302
- if (unlikely (do_realtime ((struct timespec * )tv ) == VCLOCK_NONE ))
303
- return vdso_fallback_gtod (tv , tz );
211
+ struct timespec * ts = (struct timespec * ) tv ;
212
+
213
+ do_hres (CLOCK_REALTIME , ts );
304
214
tv -> tv_usec /= 1000 ;
305
215
}
306
216
if (unlikely (tz != NULL )) {
@@ -320,7 +230,7 @@ int gettimeofday(struct timeval *, struct timezone *)
320
230
notrace time_t __vdso_time (time_t * t )
321
231
{
322
232
/* This is atomic on x86 so we don't need any locks. */
323
- time_t result = READ_ONCE (gtod -> wall_time_sec );
233
+ time_t result = READ_ONCE (gtod -> basetime [ CLOCK_REALTIME ]. sec );
324
234
325
235
if (t )
326
236
* t = result ;
0 commit comments