diff options
Diffstat (limited to 'arch/x86/vdso/vclock_gettime.c')
-rw-r--r-- | arch/x86/vdso/vclock_gettime.c | 135 |
1 files changed, 67 insertions, 68 deletions
diff --git a/arch/x86/vdso/vclock_gettime.c b/arch/x86/vdso/vclock_gettime.c index 6bc0e723b6e8..885eff49d6ab 100644 --- a/arch/x86/vdso/vclock_gettime.c +++ b/arch/x86/vdso/vclock_gettime.c | |||
@@ -70,100 +70,98 @@ notrace static long vdso_fallback_gettime(long clock, struct timespec *ts) | |||
70 | return ret; | 70 | return ret; |
71 | } | 71 | } |
72 | 72 | ||
73 | notrace static long vdso_fallback_gtod(struct timeval *tv, struct timezone *tz) | ||
74 | { | ||
75 | long ret; | ||
76 | |||
77 | asm("syscall" : "=a" (ret) : | ||
78 | "0" (__NR_gettimeofday), "D" (tv), "S" (tz) : "memory"); | ||
79 | return ret; | ||
80 | } | ||
81 | |||
82 | |||
73 | notrace static inline long vgetns(void) | 83 | notrace static inline long vgetns(void) |
74 | { | 84 | { |
75 | long v; | 85 | long v; |
76 | cycles_t cycles; | 86 | cycles_t cycles; |
77 | if (gtod->clock.vclock_mode == VCLOCK_TSC) | 87 | if (gtod->clock.vclock_mode == VCLOCK_TSC) |
78 | cycles = vread_tsc(); | 88 | cycles = vread_tsc(); |
79 | else | 89 | else if (gtod->clock.vclock_mode == VCLOCK_HPET) |
80 | cycles = vread_hpet(); | 90 | cycles = vread_hpet(); |
91 | else | ||
92 | return 0; | ||
81 | v = (cycles - gtod->clock.cycle_last) & gtod->clock.mask; | 93 | v = (cycles - gtod->clock.cycle_last) & gtod->clock.mask; |
82 | return (v * gtod->clock.mult) >> gtod->clock.shift; | 94 | return (v * gtod->clock.mult) >> gtod->clock.shift; |
83 | } | 95 | } |
84 | 96 | ||
85 | notrace static noinline int do_realtime(struct timespec *ts) | 97 | /* Code size doesn't matter (vdso is 4k anyway) and this is faster. */ |
98 | notrace static int __always_inline do_realtime(struct timespec *ts) | ||
86 | { | 99 | { |
87 | unsigned long seq, ns; | 100 | unsigned long seq, ns; |
101 | int mode; | ||
102 | |||
88 | do { | 103 | do { |
89 | seq = read_seqbegin(>od->lock); | 104 | seq = read_seqcount_begin(>od->seq); |
105 | mode = gtod->clock.vclock_mode; | ||
90 | ts->tv_sec = gtod->wall_time_sec; | 106 | ts->tv_sec = gtod->wall_time_sec; |
91 | ts->tv_nsec = gtod->wall_time_nsec; | 107 | ts->tv_nsec = gtod->wall_time_nsec; |
92 | ns = vgetns(); | 108 | ns = vgetns(); |
93 | } while (unlikely(read_seqretry(>od->lock, seq))); | 109 | } while (unlikely(read_seqcount_retry(>od->seq, seq))); |
110 | |||
94 | timespec_add_ns(ts, ns); | 111 | timespec_add_ns(ts, ns); |
95 | return 0; | 112 | return mode; |
96 | } | 113 | } |
97 | 114 | ||
98 | notrace static noinline int do_monotonic(struct timespec *ts) | 115 | notrace static int do_monotonic(struct timespec *ts) |
99 | { | 116 | { |
100 | unsigned long seq, ns, secs; | 117 | unsigned long seq, ns; |
118 | int mode; | ||
119 | |||
101 | do { | 120 | do { |
102 | seq = read_seqbegin(>od->lock); | 121 | seq = read_seqcount_begin(>od->seq); |
103 | secs = gtod->wall_time_sec; | 122 | mode = gtod->clock.vclock_mode; |
104 | ns = gtod->wall_time_nsec + vgetns(); | 123 | ts->tv_sec = gtod->monotonic_time_sec; |
105 | secs += gtod->wall_to_monotonic.tv_sec; | 124 | ts->tv_nsec = gtod->monotonic_time_nsec; |
106 | ns += gtod->wall_to_monotonic.tv_nsec; | 125 | ns = vgetns(); |
107 | } while (unlikely(read_seqretry(>od->lock, seq))); | 126 | } while (unlikely(read_seqcount_retry(>od->seq, seq))); |
108 | 127 | timespec_add_ns(ts, ns); | |
109 | /* wall_time_nsec, vgetns(), and wall_to_monotonic.tv_nsec | ||
110 | * are all guaranteed to be nonnegative. | ||
111 | */ | ||
112 | while (ns >= NSEC_PER_SEC) { | ||
113 | ns -= NSEC_PER_SEC; | ||
114 | ++secs; | ||
115 | } | ||
116 | ts->tv_sec = secs; | ||
117 | ts->tv_nsec = ns; | ||
118 | 128 | ||
119 | return 0; | 129 | return mode; |
120 | } | 130 | } |
121 | 131 | ||
122 | notrace static noinline int do_realtime_coarse(struct timespec *ts) | 132 | notrace static int do_realtime_coarse(struct timespec *ts) |
123 | { | 133 | { |
124 | unsigned long seq; | 134 | unsigned long seq; |
125 | do { | 135 | do { |
126 | seq = read_seqbegin(>od->lock); | 136 | seq = read_seqcount_begin(>od->seq); |
127 | ts->tv_sec = gtod->wall_time_coarse.tv_sec; | 137 | ts->tv_sec = gtod->wall_time_coarse.tv_sec; |
128 | ts->tv_nsec = gtod->wall_time_coarse.tv_nsec; | 138 | ts->tv_nsec = gtod->wall_time_coarse.tv_nsec; |
129 | } while (unlikely(read_seqretry(>od->lock, seq))); | 139 | } while (unlikely(read_seqcount_retry(>od->seq, seq))); |
130 | return 0; | 140 | return 0; |
131 | } | 141 | } |
132 | 142 | ||
133 | notrace static noinline int do_monotonic_coarse(struct timespec *ts) | 143 | notrace static int do_monotonic_coarse(struct timespec *ts) |
134 | { | 144 | { |
135 | unsigned long seq, ns, secs; | 145 | unsigned long seq; |
136 | do { | 146 | do { |
137 | seq = read_seqbegin(>od->lock); | 147 | seq = read_seqcount_begin(>od->seq); |
138 | secs = gtod->wall_time_coarse.tv_sec; | 148 | ts->tv_sec = gtod->monotonic_time_coarse.tv_sec; |
139 | ns = gtod->wall_time_coarse.tv_nsec; | 149 | ts->tv_nsec = gtod->monotonic_time_coarse.tv_nsec; |
140 | secs += gtod->wall_to_monotonic.tv_sec; | 150 | } while (unlikely(read_seqcount_retry(>od->seq, seq))); |
141 | ns += gtod->wall_to_monotonic.tv_nsec; | ||
142 | } while (unlikely(read_seqretry(>od->lock, seq))); | ||
143 | |||
144 | /* wall_time_nsec and wall_to_monotonic.tv_nsec are | ||
145 | * guaranteed to be between 0 and NSEC_PER_SEC. | ||
146 | */ | ||
147 | if (ns >= NSEC_PER_SEC) { | ||
148 | ns -= NSEC_PER_SEC; | ||
149 | ++secs; | ||
150 | } | ||
151 | ts->tv_sec = secs; | ||
152 | ts->tv_nsec = ns; | ||
153 | 151 | ||
154 | return 0; | 152 | return 0; |
155 | } | 153 | } |
156 | 154 | ||
157 | notrace int __vdso_clock_gettime(clockid_t clock, struct timespec *ts) | 155 | notrace int __vdso_clock_gettime(clockid_t clock, struct timespec *ts) |
158 | { | 156 | { |
157 | int ret = VCLOCK_NONE; | ||
158 | |||
159 | switch (clock) { | 159 | switch (clock) { |
160 | case CLOCK_REALTIME: | 160 | case CLOCK_REALTIME: |
161 | if (likely(gtod->clock.vclock_mode != VCLOCK_NONE)) | 161 | ret = do_realtime(ts); |
162 | return do_realtime(ts); | ||
163 | break; | 162 | break; |
164 | case CLOCK_MONOTONIC: | 163 | case CLOCK_MONOTONIC: |
165 | if (likely(gtod->clock.vclock_mode != VCLOCK_NONE)) | 164 | ret = do_monotonic(ts); |
166 | return do_monotonic(ts); | ||
167 | break; | 165 | break; |
168 | case CLOCK_REALTIME_COARSE: | 166 | case CLOCK_REALTIME_COARSE: |
169 | return do_realtime_coarse(ts); | 167 | return do_realtime_coarse(ts); |
@@ -171,32 +169,33 @@ notrace int __vdso_clock_gettime(clockid_t clock, struct timespec *ts) | |||
171 | return do_monotonic_coarse(ts); | 169 | return do_monotonic_coarse(ts); |
172 | } | 170 | } |
173 | 171 | ||
174 | return vdso_fallback_gettime(clock, ts); | 172 | if (ret == VCLOCK_NONE) |
173 | return vdso_fallback_gettime(clock, ts); | ||
174 | return 0; | ||
175 | } | 175 | } |
176 | int clock_gettime(clockid_t, struct timespec *) | 176 | int clock_gettime(clockid_t, struct timespec *) |
177 | __attribute__((weak, alias("__vdso_clock_gettime"))); | 177 | __attribute__((weak, alias("__vdso_clock_gettime"))); |
178 | 178 | ||
179 | notrace int __vdso_gettimeofday(struct timeval *tv, struct timezone *tz) | 179 | notrace int __vdso_gettimeofday(struct timeval *tv, struct timezone *tz) |
180 | { | 180 | { |
181 | long ret; | 181 | long ret = VCLOCK_NONE; |
182 | if (likely(gtod->clock.vclock_mode != VCLOCK_NONE)) { | 182 | |
183 | if (likely(tv != NULL)) { | 183 | if (likely(tv != NULL)) { |
184 | BUILD_BUG_ON(offsetof(struct timeval, tv_usec) != | 184 | BUILD_BUG_ON(offsetof(struct timeval, tv_usec) != |
185 | offsetof(struct timespec, tv_nsec) || | 185 | offsetof(struct timespec, tv_nsec) || |
186 | sizeof(*tv) != sizeof(struct timespec)); | 186 | sizeof(*tv) != sizeof(struct timespec)); |
187 | do_realtime((struct timespec *)tv); | 187 | ret = do_realtime((struct timespec *)tv); |
188 | tv->tv_usec /= 1000; | 188 | tv->tv_usec /= 1000; |
189 | } | ||
190 | if (unlikely(tz != NULL)) { | ||
191 | /* Avoid memcpy. Some old compilers fail to inline it */ | ||
192 | tz->tz_minuteswest = gtod->sys_tz.tz_minuteswest; | ||
193 | tz->tz_dsttime = gtod->sys_tz.tz_dsttime; | ||
194 | } | ||
195 | return 0; | ||
196 | } | 189 | } |
197 | asm("syscall" : "=a" (ret) : | 190 | if (unlikely(tz != NULL)) { |
198 | "0" (__NR_gettimeofday), "D" (tv), "S" (tz) : "memory"); | 191 | /* Avoid memcpy. Some old compilers fail to inline it */ |
199 | return ret; | 192 | tz->tz_minuteswest = gtod->sys_tz.tz_minuteswest; |
193 | tz->tz_dsttime = gtod->sys_tz.tz_dsttime; | ||
194 | } | ||
195 | |||
196 | if (ret == VCLOCK_NONE) | ||
197 | return vdso_fallback_gtod(tv, tz); | ||
198 | return 0; | ||
200 | } | 199 | } |
201 | int gettimeofday(struct timeval *, struct timezone *) | 200 | int gettimeofday(struct timeval *, struct timezone *) |
202 | __attribute__((weak, alias("__vdso_gettimeofday"))); | 201 | __attribute__((weak, alias("__vdso_gettimeofday"))); |