Index: sys/compat/linux/linux_time.c =================================================================== --- sys/compat/linux/linux_time.c +++ sys/compat/linux/linux_time.c @@ -39,8 +39,11 @@ #include #include +#include #include #include +#include +#include #include #include #include @@ -59,7 +62,7 @@ #endif #include -#include +#include /* DTrace init */ LIN_SDT_PROVIDER_DECLARE(LINUX_DTRACE); @@ -120,7 +123,7 @@ struct timespec *); static int linux_to_native_timespec(struct timespec *, struct l_timespec *); -static int linux_to_native_clockid(clockid_t *, clockid_t); +static int linux_to_native_clockid(struct thread *, clockid_t *, clockid_t); static void native_to_linux_timespec(struct l_timespec *ltp, struct timespec *ntp) @@ -152,11 +155,55 @@ } static int -linux_to_native_clockid(clockid_t *n, clockid_t l) +linux_to_native_clockid(struct thread *td, clockid_t *n, clockid_t l) { + pid_t pid; + lwpid_t tid; + clockid_t clockwhich; LIN_SDT_PROBE2(time, linux_to_native_clockid, entry, n, l); + if (l < 0) { + /* cpu-clock */ + if ((l & LINUX_CLOCKFD_MASK) == LINUX_CLOCKFD) + return (EINVAL); + if (LINUX_CPUCLOCK_WHICH(l) >= LINUX_CPUCLOCK_MAX) + return (EINVAL); + + if (LINUX_CPUCLOCK_PERTHREAD(l)) { + tid = LINUX_CPUCLOCK_ID(l); + if (tid != 0 && tid != td->td_tid) + return (EINVAL); + /* + * Unlike FreeBSD Linux allows watching + * on other thread in the thread group. + * XXX. We are not now. + */ + *n = CLOCK_THREAD_CPUTIME_ID; + } else { + pid = LINUX_CPUCLOCK_ID(l); + if (pid != 0 && pid != td->td_proc->p_pid) + return (EINVAL); + + clockwhich = LINUX_CPUCLOCK_WHICH(l); + switch (clockwhich) { + case LINUX_CPUCLOCK_PROF: + *n = CLOCK_PROF; + break; + case LINUX_CPUCLOCK_VIRT: + *n = CLOCK_VIRTUAL; + break; + case LINUX_CPUCLOCK_SCHED: + *n = CLOCK_PROCESS_CPUTIME_ID; + break; + default: + return (EINVAL); + break; + } + } + return (0); + } + switch (l) { case LINUX_CLOCK_REALTIME: *n = CLOCK_REALTIME; @@ -164,10 +211,18 @@ case LINUX_CLOCK_MONOTONIC: *n = CLOCK_MONOTONIC; break; - case LINUX_CLOCK_PROCESS_CPUTIME_ID: - case LINUX_CLOCK_THREAD_CPUTIME_ID: - case LINUX_CLOCK_REALTIME_HR: - case LINUX_CLOCK_MONOTONIC_HR: + case LINUX_CLOCK_REALTIME_COARSE: + *n = CLOCK_REALTIME_FAST; + break; + case LINUX_CLOCK_MONOTONIC_COARSE: + *n = CLOCK_MONOTONIC_FAST; + break; + case LINUX_CLOCK_MONOTONIC_RAW: + case LINUX_CLOCK_BOOTTIME: + case LINUX_CLOCK_REALTIME_ALARM: + case LINUX_CLOCK_BOOTTIME_ALARM: + case LINUX_CLOCK_SGI_CYCLE: + case LINUX_CLOCK_TAI: LIN_SDT_PROBE1(time, linux_to_native_clockid, unsupported_clockid, l); LIN_SDT_PROBE1(time, linux_to_native_clockid, return, EINVAL); @@ -189,20 +244,62 @@ linux_clock_gettime(struct thread *td, struct linux_clock_gettime_args *args) { struct l_timespec lts; - int error; - clockid_t nwhich = 0; /* XXX: GCC */ struct timespec tp; + struct rusage ru; + struct proc *p; + int error, clockwhich; + clockid_t nwhich = 0; /* XXX: GCC */ LIN_SDT_PROBE2(time, linux_clock_gettime, entry, args->which, args->tp); - error = linux_to_native_clockid(&nwhich, args->which); + error = linux_to_native_clockid(td, &nwhich, args->which); if (error != 0) { LIN_SDT_PROBE1(time, linux_clock_gettime, conversion_error, error); LIN_SDT_PROBE1(time, linux_clock_gettime, return, error); return (error); } - error = kern_clock_gettime(td, nwhich, &tp); + + switch (nwhich) { + case CLOCK_THREAD_CPUTIME_ID: + clockwhich = LINUX_CPUCLOCK_WHICH(args->which); + p = td->td_proc; + + switch (clockwhich) { + case LINUX_CPUCLOCK_PROF: + PROC_LOCK(p); + PROC_STATLOCK(p); + thread_lock(td); + rufetchtd(td, &ru); + thread_unlock(td); + PROC_STATUNLOCK(p); + PROC_UNLOCK(p); + timevaladd(&ru.ru_utime, &ru.ru_stime); + TIMEVAL_TO_TIMESPEC(&ru.ru_utime, &tp); + break; + case LINUX_CPUCLOCK_VIRT: + PROC_LOCK(p); + PROC_STATLOCK(p); + thread_lock(td); + rufetchtd(td, &ru); + thread_unlock(td); + PROC_STATUNLOCK(p); + PROC_UNLOCK(p); + TIMEVAL_TO_TIMESPEC(&ru.ru_utime, &tp); + break; + case LINUX_CPUCLOCK_SCHED: + error = kern_clock_gettime(td, nwhich, &tp); + break; + default: + return (EINVAL); + break; + } + break; + + default: + error = kern_clock_gettime(td, nwhich, &tp); + break; + } if (error != 0) { LIN_SDT_PROBE1(time, linux_clock_gettime, gettime_error, error); LIN_SDT_PROBE1(time, linux_clock_gettime, return, error); @@ -228,7 +325,7 @@ LIN_SDT_PROBE2(time, linux_clock_settime, entry, args->which, args->tp); - error = linux_to_native_clockid(&nwhich, args->which); + error = linux_to_native_clockid(td, &nwhich, args->which); if (error != 0) { LIN_SDT_PROBE1(time, linux_clock_settime, conversion_error, error); @@ -262,24 +359,49 @@ { struct timespec ts; struct l_timespec lts; - int error; + int error, clockwhich; clockid_t nwhich = 0; /* XXX: GCC */ LIN_SDT_PROBE2(time, linux_clock_getres, entry, args->which, args->tp); + error = linux_to_native_clockid(td, &nwhich, args->which); + if (error != 0) { + LIN_SDT_PROBE1(time, linux_clock_getres, conversion_error, + error); + LIN_SDT_PROBE1(time, linux_clock_getres, return, error); + return (error); + } if (args->tp == NULL) { LIN_SDT_PROBE0(time, linux_clock_getres, nullcall); LIN_SDT_PROBE1(time, linux_clock_getres, return, 0); return (0); } - error = linux_to_native_clockid(&nwhich, args->which); - if (error != 0) { - LIN_SDT_PROBE1(time, linux_clock_getres, conversion_error, - error); - LIN_SDT_PROBE1(time, linux_clock_getres, return, error); - return (error); + switch (nwhich) { + case CLOCK_THREAD_CPUTIME_ID: + clockwhich = LINUX_CPUCLOCK_WHICH(args->which); + + switch (clockwhich) { + case LINUX_CPUCLOCK_PROF: + error = kern_clock_gettime(td, CLOCK_PROF, &ts); + break; + case LINUX_CPUCLOCK_VIRT: + error = kern_clock_gettime(td, CLOCK_VIRTUAL, &ts); + break; + case LINUX_CPUCLOCK_SCHED: + error = kern_clock_gettime(td, nwhich, &ts); + break; + default: + return (EINVAL); + break; + } + break; + + default: + error = kern_clock_gettime(td, nwhich, &ts); + break; } + error = kern_clock_getres(td, nwhich, &ts); if (error != 0) { LIN_SDT_PROBE1(time, linux_clock_getres, getres_error, error); Index: sys/compat/linux/linux_timer.h =================================================================== --- sys/compat/linux/linux_timer.h +++ sys/compat/linux/linux_timer.h @@ -56,6 +56,23 @@ #define LINUX_CLOCK_SGI_CYCLE 10 #define LINUX_CLOCK_TAI 11 +#define LINUX_CPUCLOCK_PERTHREAD_MASK 4 +#define LINUX_CPUCLOCK_MASK 3 +#define LINUX_CPUCLOCK_WHICH(clock) \ + ((clock) & (clockid_t) LINUX_CPUCLOCK_MASK) +#define LINUX_CPUCLOCK_PROF 0 +#define LINUX_CPUCLOCK_VIRT 1 +#define LINUX_CPUCLOCK_SCHED 2 +#define LINUX_CPUCLOCK_MAX 3 +#define LINUX_CLOCKFD LINUX_CPUCLOCK_MAX +#define LINUX_CLOCKFD_MASK \ + (LINUX_CPUCLOCK_PERTHREAD_MASK|LINUX_CPUCLOCK_MASK) + +#define LINUX_CPUCLOCK_ID(clock) ((id_t) ~((clock) >> 3)) +#define LINUX_CPUCLOCK_PERTHREAD(clock) \ + (((clock) & (clockid_t) LINUX_CPUCLOCK_PERTHREAD_MASK) != 0) + + #define L_SIGEV_SIGNAL 0 #define L_SIGEV_NONE 1 #define L_SIGEV_THREAD 2