Skip to content

Commit

Permalink
OS-3271 lxbrand mssing clock_gettime and timer functionality
Browse files Browse the repository at this point in the history
  • Loading branch information
jjelinek committed Dec 3, 2014
1 parent a8a351d commit 0607727
Show file tree
Hide file tree
Showing 3 changed files with 183 additions and 14 deletions.
171 changes: 167 additions & 4 deletions usr/src/lib/brand/lx/lx_brand/common/clock.c
Expand Up @@ -28,13 +28,38 @@
#include <errno.h>
#include <string.h>
#include <time.h>
#include <sys/resource.h>
#include <sys/lx_misc.h>
#include <sys/lx_syscall.h>
#include <lx_signum.h>

/*
* 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[] = {
Expand All @@ -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);

Expand Down Expand Up @@ -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);
}
20 changes: 10 additions & 10 deletions usr/src/lib/brand/lx/lx_brand/common/lx_brand.c
Expand Up @@ -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 */
Expand Down Expand Up @@ -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 */
Expand Down
6 changes: 6 additions & 0 deletions usr/src/lib/brand/lx/lx_brand/sys/lx_syscall.h
Expand Up @@ -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);
Expand Down

0 comments on commit 0607727

Please sign in to comment.