1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 * Copyright 2015 Joyent, Inc. 26 */ 27 28 #include <errno.h> 29 #include <string.h> 30 #include <time.h> 31 #include <sys/resource.h> 32 #include <sys/lx_misc.h> 33 #include <sys/lx_syscall.h> 34 #include <lx_signum.h> 35 36 /* 37 * Translating from the Linux clock types to the Illumos types is a bit of a 38 * mess. 39 * 40 * Linux uses different values for it clock identifiers, so we have to do basic 41 * translations between the two. Thankfully, both Linux and Illumos implement 42 * the same POSIX SUSv3 clock types, so the semantics should be identical. 43 * 44 * However, CLOCK_REALTIME and CLOCK_HIGHRES (CLOCK_MONOTONIC) are the only two 45 * clock backends currently implemented on Illumos. Functions in the kernel 46 * that use the CLOCK_BACKEND macro will return an error for any clock type 47 * that does not exist in the clock_backend array. These functions are 48 * clock_settime, clock_gettime, clock_getres and timer_create. 49 * 50 * For reference, the kernel's clock_backend array looks like this: 51 * 52 * clock_backend[CLOCK_MAX] (6 entries) 53 * 0 __CLOCK_REALTIME0 valid ptr. (obs. same as CLOCK_REALTIME) 54 * 1 CLOCK_VIRTUAL NULL 55 * 2 CLOCK_THREAD_CPUTIME_ID NULL 56 * 3 CLOCK_REALTIME valid ptr. 57 * 4 CLOCK_MONOTONIC (CLOCK_HIGHRES) valid ptr. 58 * 5 CLOCK_PROCESS_CPUTIME_ID NULL 59 * 60 * See the comment on clock_highres_timer_create for full details but a zone 61 * needs the proc_clock_highres privilege to use the CLOCK_HIGHRES clock so it 62 * will generally be unusable by lx for timer_create. 63 */ 64 65 static int ltos_clock[] = { 66 CLOCK_REALTIME, /* LX_CLOCK_REALTIME */ 67 CLOCK_HIGHRES, /* LX_CLOCK_MONOTONIC */ 68 CLOCK_PROCESS_CPUTIME_ID, /* LX_CLOCK_PROCESS_CPUTIME_ID */ 69 CLOCK_THREAD_CPUTIME_ID, /* LX_CLOCK_THREAD_CPUTIME_ID */ 70 CLOCK_HIGHRES, /* LX_CLOCK_MONOTONIC_RAW */ 71 CLOCK_REALTIME, /* LX_CLOCK_REALTIME_COARSE */ 72 CLOCK_HIGHRES /* LX_CLOCK_MONOTONIC_COARSE */ 73 }; 74 75 /* 76 * Since the Illumos CLOCK_HIGHRES clock requires elevated privs, which can 77 * lead to a DOS, we use the only other option (CLOCK_REALTIME) when given 78 * LX_CLOCK_MONOTONIC. 79 */ 80 static int ltos_timer[] = { 81 CLOCK_REALTIME, 82 CLOCK_REALTIME, 83 CLOCK_THREAD_CPUTIME_ID, /* XXX thread, not process but fails */ 84 CLOCK_THREAD_CPUTIME_ID, 85 CLOCK_REALTIME, 86 CLOCK_REALTIME, 87 CLOCK_REALTIME 88 }; 89 90 #define LX_CLOCK_MAX (sizeof (ltos_clock) / sizeof (ltos_clock[0])) 91 #define LX_TIMER_MAX (sizeof (ltos_timer) / sizeof (ltos_timer[0])) 92 93 #define LX_SIGEV_PAD_SIZE ((64 - \ 94 (sizeof (int) * 2 + sizeof (union sigval))) / sizeof (int)) 95 96 typedef struct { 97 union sigval lx_sigev_value; /* same layout for both */ 98 int lx_sigev_signo; 99 int lx_sigev_notify; 100 union { 101 int lx_pad[LX_SIGEV_PAD_SIZE]; 102 int lx_tid; 103 struct { 104 void (*lx_notify_function)(union sigval); 105 void *lx_notify_attribute; 106 } lx_sigev_thread; 107 } lx_sigev_un; 108 } lx_sigevent_t; 109 110 /* sigevent sigev_notify conversion table */ 111 static int ltos_sigev[] = { 112 SIGEV_SIGNAL, 113 SIGEV_NONE, 114 SIGEV_THREAD, 115 0, /* Linux skips event 3 */ 116 SIGEV_THREAD /* the Linux SIGEV_THREAD_ID */ 117 }; 118 119 #define LX_SIGEV_MAX (sizeof (ltos_sigev) / sizeof (ltos_sigev[0])) 120 121 long 122 lx_clock_nanosleep(int clock, int flags, struct timespec *rqtp, 123 struct timespec *rmtp) 124 { 125 int ret = 0; 126 int err; 127 struct timespec rqt, rmt; 128 129 if (clock < 0 || clock >= LX_CLOCK_MAX) 130 return (-EINVAL); 131 132 if (uucopy(rqtp, &rqt, sizeof (struct timespec)) < 0) 133 return (-EFAULT); 134 135 /* the TIMER_RELTIME and TIMER_ABSTIME flags are the same on Linux */ 136 if ((err = clock_nanosleep(ltos_clock[clock], flags, &rqt, &rmt)) 137 != 0) { 138 if (err != EINTR) 139 return (-err); 140 ret = -EINTR; 141 /* 142 * We fall through in case we have to pass back the remaining 143 * time. 144 */ 145 } 146 147 /* 148 * Only copy values to rmtp if the timer is TIMER_RELTIME and rmtp is 149 * non-NULL. 150 */ 151 if (((flags & TIMER_RELTIME) == TIMER_RELTIME) && (rmtp != NULL) && 152 (uucopy(&rmt, rmtp, sizeof (struct timespec)) < 0)) 153 return (-EFAULT); 154 155 return (ret); 156 } 157 158 /*ARGSUSED*/ 159 long 160 lx_adjtimex(void *tp) 161 { 162 return (-EPERM); 163 } 164 165 /* 166 * The Illumos timer_create man page says it accepts the following clocks: 167 * CLOCK_REALTIME (3) wall clock 168 * CLOCK_VIRTUAL (1) user CPU usage clock - No Backend 169 * CLOCK_PROF (2) user and system CPU usage clock - No Backend 170 * CLOCK_HIGHRES (4) non-adjustable, high-resolution clock 171 * However, in reality the Illumos timer_create only accepts CLOCK_REALTIME 172 * and CLOCK_HIGHRES, and since we can't use CLOCK_HIGHRES in a zone, we're 173 * down to one clock. 174 */ 175 long 176 lx_timer_create(int clock, struct sigevent *lx_sevp, timer_t *tid) 177 { 178 lx_sigevent_t lev; 179 struct sigevent sev; 180 181 if (clock < 0 || clock >= LX_TIMER_MAX) 182 return (-EINVAL); 183 184 /* We have to convert the Linux sigevent layout to the Illumos layout */ 185 if (uucopy(lx_sevp, &lev, sizeof (lev)) < 0) 186 return (-EFAULT); 187 188 if (lev.lx_sigev_notify < 0 || lev.lx_sigev_notify > LX_SIGEV_MAX) 189 return (-EINVAL); 190 191 sev.sigev_notify = ltos_sigev[lev.lx_sigev_notify]; 192 sev.sigev_signo = ltos_signo[lev.lx_sigev_signo]; 193 sev.sigev_value = lev.lx_sigev_value; 194 195 /* 196 * The sigevent sigev_notify_function and sigev_notify_attributes 197 * members are not used by timer_create, so no conversion is needed. 198 */ 199 200 return ((timer_create(ltos_timer[clock], &sev, tid) < 0) ? -errno : 0); 201 } 202 203 long 204 lx_timer_settime(timer_t tid, int flags, struct itimerspec *new_val, 205 struct itimerspec *old_val) 206 { 207 return ((timer_settime(tid, flags, new_val, old_val) < 0) ? -errno : 0); 208 } 209 210 long 211 lx_timer_gettime(timer_t tid, struct itimerspec *val) 212 { 213 return ((timer_gettime(tid, val) < 0) ? -errno : 0); 214 } 215 216 long 217 lx_timer_getoverrun(timer_t tid) 218 { 219 int val; 220 221 val = timer_getoverrun(tid); 222 return ((val < 0) ? -errno : val); 223 } 224 225 long 226 lx_timer_delete(timer_t tid) 227 { 228 return ((timer_delete(tid) < 0) ? -errno : 0); 229 }