diff options
| -rw-r--r-- | src/time/clock_gettime.c | 61 | 
1 files changed, 39 insertions, 22 deletions
| diff --git a/src/time/clock_gettime.c b/src/time/clock_gettime.c index 1572de0e..24128804 100644 --- a/src/time/clock_gettime.c +++ b/src/time/clock_gettime.c @@ -5,37 +5,54 @@  #include "libc.h"  #include "atomic.h" -static int sc_clock_gettime(clockid_t clk, struct timespec *ts) +#ifdef VDSO_CGT_SYM + +void *__vdsosym(const char *, const char *); + +static void *volatile vdso_func; + +static int cgt_init(clockid_t clk, struct timespec *ts)  { -	int r = __syscall(SYS_clock_gettime, clk, ts); -	if (!r) return r; -	if (r == -ENOSYS) { -		if (clk == CLOCK_REALTIME) { -			__syscall(SYS_gettimeofday, ts, 0); -			ts->tv_nsec = (int)ts->tv_nsec * 1000; -			return 0; -		} -		r = -EINVAL; -	} -	errno = -r; -	return -1; +	void *p = __vdsosym(VDSO_CGT_VER, VDSO_CGT_SYM); +	int (*f)(clockid_t, struct timespec *) = +		(int (*)(clockid_t, struct timespec *))p; +	a_cas_p(&vdso_func, (void *)cgt_init, p); +	return f ? f(clk, ts) : -ENOSYS;  } -void *__vdsosym(const char *, const char *); +static void *volatile vdso_func = (void *)cgt_init; + +#endif  int __clock_gettime(clockid_t clk, struct timespec *ts)  { +	int r; +  #ifdef VDSO_CGT_SYM -	static int (*volatile cgt)(clockid_t, struct timespec *); -	if (!cgt) { -		void *f = __vdsosym(VDSO_CGT_VER, VDSO_CGT_SYM); -		if (!f) f = (void *)sc_clock_gettime; -		a_cas_p(&cgt, 0, f); +	int (*f)(clockid_t, struct timespec *) = +		(int (*)(clockid_t, struct timespec *))vdso_func; +	if (f) { +		r = f(clk, ts); +		if (!r) return r; +		if (r == -EINVAL) return __syscall_ret(r); +		/* Fall through on errors other than EINVAL. Some buggy +		 * vdso implementations return ENOSYS for clocks they +		 * can't handle, rather than making the syscall. This +		 * also handles the case where cgt_init fails to find +		 * a vdso function to use. */  	} -	return cgt(clk, ts); -#else -	return sc_clock_gettime(clk, ts);  #endif + +	r = __syscall(SYS_clock_gettime, clk, ts); +	if (r == -ENOSYS) { +		if (clk == CLOCK_REALTIME) { +			__syscall(SYS_gettimeofday, ts, 0); +			ts->tv_nsec = (int)ts->tv_nsec * 1000; +			return 0; +		} +		r = -EINVAL; +	} +	return __syscall_ret(r);  }  weak_alias(__clock_gettime, clock_gettime); | 
