diff --git a/usr/src/lib/brand/lx/lx_brand/common/clock.c b/usr/src/lib/brand/lx/lx_brand/common/clock.c index 80149f7e3381..8ba265d2ed24 100644 --- a/usr/src/lib/brand/lx/lx_brand/common/clock.c +++ b/usr/src/lib/brand/lx/lx_brand/common/clock.c @@ -28,13 +28,38 @@ #include #include #include +#include #include #include +#include /* + * Translating from the Linux clock types to the Illumos types is a bit of a + * mess. + * * Linux uses different values for it clock identifiers, so we have to do basic - * translations between the two. Thankfully, both Linux and Solaris implement + * translations between the two. Thankfully, both Linux and Illumos implement * the same POSIX SUSv3 clock types, so the semantics should be identical. + * + * However, CLOCK_REALTIME and CLOCK_HIGHRES (CLOCK_MONOTONIC) are the only two + * clock backends currently implemented on Illumos. Functions in the kernel + * that use the CLOCK_BACKEND macro will return an error for any clock type + * that does not exist in the clock_backend array. These functions are + * clock_settime, clock_gettime, clock_getres and timer_create. + * + * For reference, the kernel's clock_backend array looks like this: + * + * clock_backend[CLOCK_MAX] (6 entries) + * 0 __CLOCK_REALTIME0 valid ptr. (obs. same as CLOCK_REALTIME) + * 1 CLOCK_VIRTUAL NULL + * 2 CLOCK_THREAD_CPUTIME_ID NULL + * 3 CLOCK_REALTIME valid ptr. + * 4 CLOCK_MONOTONIC (CLOCK_HIGHRES) valid ptr. + * 5 CLOCK_PROCESS_CPUTIME_ID NULL + * + * See the comment on clock_highres_timer_create for full details but a zone + * needs the proc_clock_highres privilege to use the CLOCK_HIGHRES clock so it + * will generally be unusable by lx for timer_create. */ static int ltos_clock[] = { @@ -44,19 +69,91 @@ static int ltos_clock[] = { CLOCK_THREAD_CPUTIME_ID }; +/* + * Since the Illumos CLOCK_HIGHRES clock requires elevated privs, which can + * lead to a DOS, we use the only other option (CLOCK_REALTIME) when given + * LX_CLOCK_MONOTONIC. + */ +static int ltos_timer[] = { + CLOCK_REALTIME, + CLOCK_REALTIME, + CLOCK_THREAD_CPUTIME_ID, /* XXX thread, not process but fails */ + CLOCK_THREAD_CPUTIME_ID +}; + +#define LX_CLOCK_REALTIME 0 +#define LX_CLOCK_MONOTONIC 1 +#define LX_CLOCK_PROCESS_CPUTIME_ID 2 +#define LX_CLOCK_THREAD_CPUTIME_ID 3 + #define LX_CLOCK_MAX (sizeof (ltos_clock) / sizeof (ltos_clock[0])) +#define LX_TIMER_MAX (sizeof (ltos_timer) / sizeof (ltos_timer[0])) -long -lx_clock_gettime(int clock, struct timespec *tp) +#define LX_SIGEV_PAD_SIZE ((64 - \ + (sizeof (int) * 2 + sizeof (union sigval))) / sizeof (int)) + +typedef struct { + union sigval lx_sigev_value; /* same layout for both */ + int lx_sigev_signo; + int lx_sigev_notify; + union { + int lx_pad[LX_SIGEV_PAD_SIZE]; + int lx_tid; + struct { + void (*lx_notify_function)(union sigval); + void *lx_notify_attribute; + } lx_sigev_thread; + } lx_sigev_un; +} lx_sigevent_t; + +/* sigevent sigev_notify conversion table */ +static int ltos_sigev[] = { + SIGEV_SIGNAL, + SIGEV_NONE, + SIGEV_THREAD, + 0, /* Linux skips event 3 */ + SIGEV_THREAD /* the Linux SIGEV_THREAD_ID */ +}; + +#define LX_SIGEV_MAX (sizeof (ltos_sigev) / sizeof (ltos_sigev[0])) + +static long +get_cputime(int who, struct timespec *tp) { struct timespec ts; + struct rusage ru; + long ns; - if (clock < 0 || clock > LX_CLOCK_MAX) + if (getrusage(who, &ru) != 0) return (-EINVAL); + ts.tv_sec = ru.ru_utime.tv_sec + ru.ru_stime.tv_sec; + ns = (ru.ru_utime.tv_usec + ru.ru_stime.tv_usec) * 1000; + if (ns > NANOSEC) { + ts.tv_sec += 1; + ns -= NANOSEC; + } + ts.tv_nsec = ns; + return ((uucopy(&ts, tp, sizeof (struct timespec)) < 0) ? -EFAULT : 0); +} + +long +lx_clock_gettime(int clock, struct timespec *tp) +{ + struct timespec ts; + if (tp == NULL) return (-EFAULT); + if (clock == LX_CLOCK_PROCESS_CPUTIME_ID) { + return (get_cputime(RUSAGE_SELF, tp)); + } else if (clock == LX_CLOCK_THREAD_CPUTIME_ID) { + return (get_cputime(RUSAGE_LWP, tp)); + } + + if (clock < 0 || clock > LX_CLOCK_MAX) + return (-EINVAL); + if (clock_gettime(ltos_clock[clock], &ts) < 0) return (-errno); @@ -128,3 +225,69 @@ lx_adjtimex(void *tp) { return (-EPERM); } + +/* + * The Illumos timer_create man page says it accepts the following clocks: + * CLOCK_REALTIME (3) wall clock + * CLOCK_VIRTUAL (1) user CPU usage clock - No Backend + * CLOCK_PROF (2) user and system CPU usage clock - No Backend + * CLOCK_HIGHRES (4) non-adjustable, high-resolution clock + * However, in reality the Illumos timer_create only accepts CLOCK_REALTIME + * and CLOCK_HIGHRES, and since we can't use CLOCK_HIGHRES in a zone, we're + * down to one clock. + */ +long +lx_timer_create(int clock, struct sigevent *lx_sevp, timer_t *tid) +{ + lx_sigevent_t lev; + struct sigevent sev; + + if (clock < 0 || clock > LX_TIMER_MAX) + return (-EINVAL); + + /* We have to convert the Linux sigevent layout to the Illumos layout */ + if (uucopy(lx_sevp, &lev, sizeof (lev)) < 0) + return (-EFAULT); + + if (lev.lx_sigev_notify < 0 || lev.lx_sigev_notify > LX_SIGEV_MAX) + return (-EINVAL); + + sev.sigev_notify = ltos_sigev[lev.lx_sigev_notify]; + sev.sigev_signo = ltos_signo[lev.lx_sigev_signo]; + sev.sigev_value = lev.lx_sigev_value; + + /* + * The sigevent sigev_notify_function and sigev_notify_attributes + * members are not used by timer_create, so no conversion is needed. + */ + + return ((timer_create(ltos_timer[clock], &sev, tid) < 0) ? -errno : 0); +} + +long +lx_timer_settime(timer_t tid, int flags, struct itimerspec *new_val, + struct itimerspec *old_val) +{ + return ((timer_settime(tid, flags, new_val, old_val) < 0) ? -errno : 0); +} + +long +lx_timer_gettime(timer_t tid, struct itimerspec *val) +{ + return ((timer_gettime(tid, val) < 0) ? -errno : 0); +} + +long +lx_timer_getoverrun(timer_t tid) +{ + int val; + + val = timer_getoverrun(tid); + return ((val < 0) ? -errno : val); +} + +long +lx_timer_delete(timer_t tid) +{ + return ((timer_delete(tid) < 0) ? -errno : 0); +} diff --git a/usr/src/lib/brand/lx/lx_brand/common/lx_brand.c b/usr/src/lib/brand/lx/lx_brand/common/lx_brand.c index 02bbcb0b895b..305a212a34fd 100644 --- a/usr/src/lib/brand/lx/lx_brand/common/lx_brand.c +++ b/usr/src/lib/brand/lx/lx_brand/common/lx_brand.c @@ -1317,11 +1317,11 @@ static struct lx_sysent sysents[] = { {"restart_syscall", NULL, NOSYS_NULL, 0}, /* 219 */ {"semtimedop", lx_semtimedop, 0, 4}, /* 220 */ {"fadvise64", lx_fadvise64_64, 0, 4}, /* 221 */ - {"timer_create", NULL, NOSYS_UNDOC, 0}, /* 222 */ - {"timer_settime", NULL, NOSYS_UNDOC, 0}, /* 223 */ - {"timer_gettime", NULL, NOSYS_UNDOC, 0}, /* 224 */ - {"timer_getoverrun", NULL, NOSYS_UNDOC, 0}, /* 225 */ - {"timer_delete", NULL, NOSYS_UNDOC, 0}, /* 226 */ + {"timer_create", lx_timer_create, 0, 3}, /* 222 */ + {"timer_settime", lx_timer_settime, 0, 4}, /* 223 */ + {"timer_gettime", lx_timer_gettime, 0, 2}, /* 224 */ + {"timer_getoverrun", lx_timer_getoverrun, 0, 1}, /* 225 */ + {"timer_delete", lx_timer_delete, 0, 1}, /* 226 */ {"clock_settime", lx_clock_settime, 0, 2}, /* 227 */ {"clock_gettime", lx_clock_gettime, 0, 2}, /* 228 */ {"clock_getres", lx_clock_getres, 0, 2}, /* 229 */ @@ -1679,11 +1679,11 @@ static struct lx_sysent sysents[] = { {"epoll_wait", lx_epoll_wait, 0, 4}, /* 256 */ {"remap_file_pages", NULL, NOSYS_NO_EQUIV, 0}, /* 257 */ {"set_tid_address", LX_IKE(set_tid_address), LX_SYS_IKE, 1}, /* 258 */ - {"timer_create", NULL, NOSYS_UNDOC, 0}, /* 259 */ - {"timer_settime", NULL, NOSYS_UNDOC, 0}, /* 260 */ - {"timer_gettime", NULL, NOSYS_UNDOC, 0}, /* 261 */ - {"timer_getoverrun", NULL, NOSYS_UNDOC, 0}, /* 262 */ - {"timer_delete", NULL, NOSYS_UNDOC, 0}, /* 263 */ + {"timer_create", lx_timer_create, 0, 3}, /* 259 */ + {"timer_settime", lx_timer_settime, 0, 4}, /* 260 */ + {"timer_gettime", lx_timer_gettime, 0, 2}, /* 261 */ + {"timer_getoverrun", lx_timer_getoverrun, 0, 1}, /* 262 */ + {"timer_delete", lx_timer_delete, 0, 1}, /* 263 */ {"clock_settime", lx_clock_settime, 0, 2}, /* 264 */ {"clock_gettime", lx_clock_gettime, 0, 2}, /* 265 */ {"clock_getres", lx_clock_getres, 0, 2}, /* 266 */ diff --git a/usr/src/lib/brand/lx/lx_brand/sys/lx_syscall.h b/usr/src/lib/brand/lx/lx_brand/sys/lx_syscall.h index e336746d9bfc..2e7d257d2b77 100644 --- a/usr/src/lib/brand/lx/lx_brand/sys/lx_syscall.h +++ b/usr/src/lib/brand/lx/lx_brand/sys/lx_syscall.h @@ -166,6 +166,12 @@ extern long lx_clock_getres(int, struct timespec *); extern long lx_clock_nanosleep(int, int flags, struct timespec *, struct timespec *); extern long lx_adjtimex(void *); +extern long lx_timer_create(int, struct sigevent *, timer_t *); +extern long lx_timer_settime(timer_t, int, struct itimerspec *, + struct itimerspec *); +extern long lx_timer_gettime(timer_t, struct itimerspec *); +extern long lx_timer_getoverrun(timer_t); +extern long lx_timer_delete(timer_t); extern long lx_truncate(uintptr_t, uintptr_t); extern long lx_ftruncate(uintptr_t, uintptr_t);