mirror of
git://git.musl-libc.org/musl
synced 2025-02-04 13:01:37 +00:00
improve clock_gettime and adapt it to support slightly-broken vdso
these changes are motivated by a functionally similar patch by Hauke Mehrtens to address the needs of the new mips vdso clock_gettime, which wrongly fails with ENOSYS rather than falling back to making a syscall for clock ids it cannot handle from userspace. in the process of preparing to handle that case, it was noticed that the old clock_gettime use of the vdso was actually wrong with respect to error handling -- the tail call to the vdso function failed to set errno and instead returned an error code. since tail calls to vdso are no longer possible and since the plain syscall code is now needed as a fallback path anyway, it does not make sense to use a function pointer to call the plain syscall code path. instead, it's inlined at the end of the main clock_gettime function. the new code also avoids the need to test for initialization of the vdso function pointer by statically initializing it to a self-init function, and eliminates redundant loads from the volatile pointer object. finally, the use of a_cas_p on an object of type other than void *, which is not permitted aliasing, is replaced by using an object with the correct type and casting the value.
This commit is contained in:
parent
9a3b8f97a1
commit
a5ba2d7507
@ -5,10 +5,45 @@
|
||||
#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;
|
||||
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;
|
||||
}
|
||||
|
||||
static void *volatile vdso_func = (void *)cgt_init;
|
||||
|
||||
#endif
|
||||
|
||||
int __clock_gettime(clockid_t clk, struct timespec *ts)
|
||||
{
|
||||
int r;
|
||||
|
||||
#ifdef VDSO_CGT_SYM
|
||||
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. */
|
||||
}
|
||||
#endif
|
||||
|
||||
r = __syscall(SYS_clock_gettime, clk, ts);
|
||||
if (r == -ENOSYS) {
|
||||
if (clk == CLOCK_REALTIME) {
|
||||
__syscall(SYS_gettimeofday, ts, 0);
|
||||
@ -17,25 +52,7 @@ static int sc_clock_gettime(clockid_t clk, struct timespec *ts)
|
||||
}
|
||||
r = -EINVAL;
|
||||
}
|
||||
errno = -r;
|
||||
return -1;
|
||||
}
|
||||
|
||||
void *__vdsosym(const char *, const char *);
|
||||
|
||||
int __clock_gettime(clockid_t clk, struct timespec *ts)
|
||||
{
|
||||
#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);
|
||||
}
|
||||
return cgt(clk, ts);
|
||||
#else
|
||||
return sc_clock_gettime(clk, ts);
|
||||
#endif
|
||||
return __syscall_ret(r);
|
||||
}
|
||||
|
||||
weak_alias(__clock_gettime, clock_gettime);
|
||||
|
Loading…
Reference in New Issue
Block a user