Print this page
OS-???? [lx] SIGEV_THREAD_ID emulation needed


   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:


  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);


 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;


   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 <sys/syscall.h>
  29 
  30 #include <errno.h>
  31 #include <stdlib.h>
  32 #include <string.h>
  33 #include <time.h>
  34 #include <unistd.h>
  35 #include <sys/resource.h>
  36 #include <sys/lx_misc.h>
  37 #include <sys/lx_syscall.h>
  38 #include <lx_signum.h>
  39 
  40 /*
  41  * Translating from the Linux clock types to the Illumos types is a bit of a
  42  * mess.
  43  *
  44  * Linux uses different values for it clock identifiers, so we have to do basic
  45  * translations between the two.  Thankfully, both Linux and Illumos implement
  46  * the same POSIX SUSv3 clock types, so the semantics should be identical.
  47  *
  48  * However, CLOCK_REALTIME and CLOCK_HIGHRES (CLOCK_MONOTONIC) are the only two
  49  * clock backends currently implemented on Illumos. Functions in the kernel
  50  * that use the CLOCK_BACKEND macro will return an error for any clock type
  51  * that does not exist in the clock_backend array. These functions are
  52  * clock_settime, clock_gettime, clock_getres and timer_create.
  53  *
  54  * For reference, the kernel's clock_backend array looks like this:


 100 typedef struct {
 101         union sigval    lx_sigev_value; /* same layout for both */
 102         int             lx_sigev_signo;
 103         int             lx_sigev_notify;
 104         union {
 105                 int     lx_pad[LX_SIGEV_PAD_SIZE];
 106                 int     lx_tid;
 107                 struct {
 108                         void (*lx_notify_function)(union sigval);
 109                         void *lx_notify_attribute;
 110                 } lx_sigev_thread;
 111         } lx_sigev_un;
 112 } lx_sigevent_t;
 113 
 114 /* sigevent sigev_notify conversion table */
 115 static int ltos_sigev[] = {
 116         SIGEV_SIGNAL,
 117         SIGEV_NONE,
 118         SIGEV_THREAD,
 119         0,              /* Linux skips event 3 */
 120         SIGEV_THREAD    /* Linux SIGEV_THREAD_ID -- see lx_sigev_thread_id() */
 121 };
 122 
 123 #define LX_SIGEV_MAX            (sizeof (ltos_sigev) / sizeof (ltos_sigev[0]))
 124 #define LX_SIGEV_THREAD_ID      4
 125 
 126 long
 127 lx_clock_nanosleep(int clock, int flags, struct timespec *rqtp,
 128     struct timespec *rmtp)
 129 {
 130         int ret = 0;
 131         int err;
 132         struct timespec rqt, rmt;
 133 
 134         if (clock < 0 || clock >= LX_CLOCK_MAX)
 135                 return (-EINVAL);
 136 
 137         if (uucopy(rqtp, &rqt, sizeof (struct timespec)) < 0)
 138                 return (-EFAULT);
 139 
 140         /* the TIMER_RELTIME and TIMER_ABSTIME flags are the same on Linux */
 141         if ((err = clock_nanosleep(ltos_clock[clock], flags, &rqt, &rmt))
 142             != 0) {
 143                 if (err != EINTR)
 144                         return (-err);


 151 
 152         /*
 153          * Only copy values to rmtp if the timer is TIMER_RELTIME and rmtp is
 154          * non-NULL.
 155          */
 156         if (((flags & TIMER_RELTIME) == TIMER_RELTIME) && (rmtp != NULL) &&
 157             (uucopy(&rmt, rmtp, sizeof (struct timespec)) < 0))
 158                 return (-EFAULT);
 159 
 160         return (ret);
 161 }
 162 
 163 /*ARGSUSED*/
 164 long
 165 lx_adjtimex(void *tp)
 166 {
 167         return (-EPERM);
 168 }
 169 
 170 /*
 171  * Notification function for use with native SIGEV_THREAD in order to
 172  * emulate Linux SIGEV_THREAD_ID. Native SIGEV_THREAD is used as the
 173  * timer mechanism and B_SIGEV_THREAD_ID performs the actual event
 174  * delivery to the appropriate lx tid.
 175  */
 176 static void
 177 lx_sigev_thread_id(union sigval sival)
 178 {
 179         lx_sigevent_t *lev = (lx_sigevent_t *)sival.sival_ptr;
 180         syscall(SYS_brand, B_SIGEV_THREAD_ID, lev->lx_sigev_un.lx_tid,
 181             lev->lx_sigev_signo, lev->lx_sigev_value);
 182         free(lev);
 183 }
 184 
 185 
 186 /*
 187  * The Illumos timer_create man page says it accepts the following clocks:
 188  *   CLOCK_REALTIME (3) wall clock
 189  *   CLOCK_VIRTUAL (1)  user CPU usage clock - No Backend
 190  *   CLOCK_PROF (2)     user and system CPU usage clock - No Backend
 191  *   CLOCK_HIGHRES (4)  non-adjustable, high-resolution clock
 192  * However, in reality the Illumos timer_create only accepts CLOCK_REALTIME
 193  * and CLOCK_HIGHRES, and since we can't use CLOCK_HIGHRES in a zone, we're
 194  * down to one clock.
 195  */
 196 long
 197 lx_timer_create(int clock, struct sigevent *lx_sevp, timer_t *tid)
 198 {
 199         lx_sigevent_t lev;
 200         struct sigevent sev;
 201 
 202         if (clock < 0 || clock >= LX_TIMER_MAX)
 203                 return (-EINVAL);
 204 
 205         /* We have to convert the Linux sigevent layout to the Illumos layout */
 206         if (uucopy(lx_sevp, &lev, sizeof (lev)) < 0)
 207                 return (-EFAULT);
 208 
 209         if (lev.lx_sigev_notify < 0 || lev.lx_sigev_notify > LX_SIGEV_MAX)
 210                 return (-EINVAL);
 211 
 212         sev.sigev_notify = ltos_sigev[lev.lx_sigev_notify];
 213         sev.sigev_signo = ltos_signo[lev.lx_sigev_signo];
 214         sev.sigev_value = lev.lx_sigev_value;
 215 
 216         /*
 217          * Assume all Linux libc implementations map SIGEV_THREAD to
 218          * SIGEV_THREAD_ID and ignore passed-in attributes.
 219          */
 220         sev.sigev_notify_attributes = NULL;
 221 
 222         if (lev.lx_sigev_notify == LX_SIGEV_THREAD_ID) {
 223                 pid_t caller_pid = getpid();
 224                 pid_t target_pid;
 225                 int rval;
 226 
 227                 if ((rval = lx_lpid_to_spair(lev.lx_sigev_un.lx_tid,
 228                             &target_pid, NULL)) != 0) {
 229 
 230                         /*
 231                          * Attempt to stick to the defined ERRORS in
 232                          * timer_create(2).
 233                          */
 234                         if (rval == -ESRCH)
 235                                 return (-EINVAL);
 236 
 237                         return (rval);
 238                 }
 239 
 240                 /*
 241                  * The caller of SIGEV_THREAD_ID must be in the same
 242                  * process as the target thread.
 243                  */
 244                 if (caller_pid != target_pid)
 245                         return (-EINVAL);
 246 
 247                 /*
 248                  * Pass the original lx sigevent_t to the native
 249                  * notify function so that it may pass it to the lx
 250                  * helper thread. It is the responsibility of
 251                  * lx_sigev_thread_id() to free lev_copy after the
 252                  * information is relayed to lx.
 253                  */
 254                 lx_sigevent_t *lev_copy = malloc(sizeof (lx_sigevent_t));
 255                 if (lev_copy == NULL)
 256                         return (-ENOMEM);
 257 
 258                 if (uucopy(&lev, lev_copy, sizeof (lx_sigevent_t)) < 0) {
 259                         free(lev_copy);
 260                         return (-EFAULT);
 261                 }
 262 
 263                 sev.sigev_notify_function = lx_sigev_thread_id;
 264                 sev.sigev_value.sival_ptr = lev_copy;
 265         }
 266 
 267         return ((timer_create(ltos_timer[clock], &sev, tid) < 0) ? -errno : 0);
 268 }
 269 
 270 long
 271 lx_timer_settime(timer_t tid, int flags, struct itimerspec *new_val,
 272     struct itimerspec *old_val)
 273 {
 274         return ((timer_settime(tid, flags, new_val, old_val) < 0) ? -errno : 0);
 275 }
 276 
 277 long
 278 lx_timer_gettime(timer_t tid, struct itimerspec *val)
 279 {
 280         return ((timer_gettime(tid, val) < 0) ? -errno : 0);
 281 }
 282 
 283 long
 284 lx_timer_getoverrun(timer_t tid)
 285 {
 286         int val;