Print this page
OS-4514 [lx] SIGEV_THREAD_ID emulation needed
Split |
Close |
Expand all |
Collapse all |
--- old/usr/src/lib/brand/lx/lx_brand/common/clock.c
+++ new/usr/src/lib/brand/lx/lx_brand/common/clock.c
1 1 /*
2 2 * CDDL HEADER START
3 3 *
4 4 * The contents of this file are subject to the terms of the
5 5 * Common Development and Distribution License (the "License").
6 6 * You may not use this file except in compliance with the License.
7 7 *
8 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 9 * or http://www.opensolaris.org/os/licensing.
10 10 * See the License for the specific language governing permissions
11 11 * and limitations under the License.
12 12 *
13 13 * When distributing Covered Code, include this CDDL HEADER in each
14 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 15 * If applicable, add the following below this CDDL HEADER, with the
16 16 * fields enclosed by brackets "[]" replaced with your own identifying
17 17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 18 *
↓ open down ↓ |
18 lines elided |
↑ open up ↑ |
19 19 * CDDL HEADER END
20 20 */
21 21
22 22 /*
23 23 * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
24 24 * Use is subject to license terms.
25 25 * Copyright 2015 Joyent, Inc.
26 26 */
27 27
28 28 #include <errno.h>
29 +#include <stdlib.h>
29 30 #include <string.h>
30 31 #include <time.h>
32 +#include <unistd.h>
31 33 #include <sys/resource.h>
34 +#include <sys/syscall.h>
32 35 #include <sys/lx_misc.h>
33 36 #include <sys/lx_syscall.h>
34 37 #include <lx_signum.h>
35 38
36 39 /*
37 40 * Translating from the Linux clock types to the Illumos types is a bit of a
38 41 * mess.
39 42 *
40 43 * Linux uses different values for it clock identifiers, so we have to do basic
41 44 * translations between the two. Thankfully, both Linux and Illumos implement
42 45 * the same POSIX SUSv3 clock types, so the semantics should be identical.
43 46 *
44 47 * However, CLOCK_REALTIME and CLOCK_HIGHRES (CLOCK_MONOTONIC) are the only two
45 48 * clock backends currently implemented on Illumos. Functions in the kernel
46 49 * that use the CLOCK_BACKEND macro will return an error for any clock type
47 50 * that does not exist in the clock_backend array. These functions are
48 51 * clock_settime, clock_gettime, clock_getres and timer_create.
49 52 *
50 53 * For reference, the kernel's clock_backend array looks like this:
51 54 *
52 55 * clock_backend[CLOCK_MAX] (6 entries)
53 56 * 0 __CLOCK_REALTIME0 valid ptr. (obs. same as CLOCK_REALTIME)
54 57 * 1 CLOCK_VIRTUAL NULL
55 58 * 2 CLOCK_THREAD_CPUTIME_ID NULL
56 59 * 3 CLOCK_REALTIME valid ptr.
57 60 * 4 CLOCK_MONOTONIC (CLOCK_HIGHRES) valid ptr.
58 61 * 5 CLOCK_PROCESS_CPUTIME_ID NULL
59 62 *
60 63 * See the comment on clock_highres_timer_create for full details but a zone
61 64 * needs the proc_clock_highres privilege to use the CLOCK_HIGHRES clock so it
62 65 * will generally be unusable by lx for timer_create.
63 66 */
64 67
65 68 static int ltos_clock[] = {
66 69 CLOCK_REALTIME, /* LX_CLOCK_REALTIME */
67 70 CLOCK_HIGHRES, /* LX_CLOCK_MONOTONIC */
68 71 CLOCK_PROCESS_CPUTIME_ID, /* LX_CLOCK_PROCESS_CPUTIME_ID */
69 72 CLOCK_THREAD_CPUTIME_ID, /* LX_CLOCK_THREAD_CPUTIME_ID */
70 73 CLOCK_HIGHRES, /* LX_CLOCK_MONOTONIC_RAW */
71 74 CLOCK_REALTIME, /* LX_CLOCK_REALTIME_COARSE */
72 75 CLOCK_HIGHRES /* LX_CLOCK_MONOTONIC_COARSE */
73 76 };
74 77
75 78 /*
76 79 * Since the Illumos CLOCK_HIGHRES clock requires elevated privs, which can
77 80 * lead to a DOS, we use the only other option (CLOCK_REALTIME) when given
78 81 * LX_CLOCK_MONOTONIC.
79 82 */
80 83 static int ltos_timer[] = {
81 84 CLOCK_REALTIME,
82 85 CLOCK_REALTIME,
83 86 CLOCK_THREAD_CPUTIME_ID, /* XXX thread, not process but fails */
84 87 CLOCK_THREAD_CPUTIME_ID,
85 88 CLOCK_REALTIME,
86 89 CLOCK_REALTIME,
87 90 CLOCK_REALTIME
88 91 };
89 92
90 93 #define LX_CLOCK_MAX (sizeof (ltos_clock) / sizeof (ltos_clock[0]))
91 94 #define LX_TIMER_MAX (sizeof (ltos_timer) / sizeof (ltos_timer[0]))
92 95
93 96 #define LX_SIGEV_PAD_SIZE ((64 - \
94 97 (sizeof (int) * 2 + sizeof (union sigval))) / sizeof (int))
95 98
96 99 typedef struct {
97 100 union sigval lx_sigev_value; /* same layout for both */
98 101 int lx_sigev_signo;
99 102 int lx_sigev_notify;
100 103 union {
101 104 int lx_pad[LX_SIGEV_PAD_SIZE];
102 105 int lx_tid;
103 106 struct {
104 107 void (*lx_notify_function)(union sigval);
105 108 void *lx_notify_attribute;
↓ open down ↓ |
64 lines elided |
↑ open up ↑ |
106 109 } lx_sigev_thread;
107 110 } lx_sigev_un;
108 111 } lx_sigevent_t;
109 112
110 113 /* sigevent sigev_notify conversion table */
111 114 static int ltos_sigev[] = {
112 115 SIGEV_SIGNAL,
113 116 SIGEV_NONE,
114 117 SIGEV_THREAD,
115 118 0, /* Linux skips event 3 */
116 - SIGEV_THREAD /* the Linux SIGEV_THREAD_ID */
119 + SIGEV_THREAD /* Linux SIGEV_THREAD_ID -- see lx_sigev_thread_id() */
117 120 };
118 121
119 -#define LX_SIGEV_MAX (sizeof (ltos_sigev) / sizeof (ltos_sigev[0]))
122 +#define LX_SIGEV_MAX (sizeof (ltos_sigev) / sizeof (ltos_sigev[0]))
123 +#define LX_SIGEV_THREAD_ID 4
120 124
121 125 long
122 126 lx_clock_nanosleep(int clock, int flags, struct timespec *rqtp,
123 127 struct timespec *rmtp)
124 128 {
125 129 int ret = 0;
126 130 int err;
127 131 struct timespec rqt, rmt;
128 132
129 133 if (clock < 0 || clock >= LX_CLOCK_MAX)
130 134 return (-EINVAL);
131 135
132 136 if (uucopy(rqtp, &rqt, sizeof (struct timespec)) < 0)
133 137 return (-EFAULT);
134 138
135 139 /* the TIMER_RELTIME and TIMER_ABSTIME flags are the same on Linux */
136 140 if ((err = clock_nanosleep(ltos_clock[clock], flags, &rqt, &rmt))
137 141 != 0) {
138 142 if (err != EINTR)
139 143 return (-err);
140 144 ret = -EINTR;
141 145 /*
142 146 * We fall through in case we have to pass back the remaining
143 147 * time.
144 148 */
145 149 }
146 150
147 151 /*
148 152 * Only copy values to rmtp if the timer is TIMER_RELTIME and rmtp is
149 153 * non-NULL.
150 154 */
151 155 if (((flags & TIMER_RELTIME) == TIMER_RELTIME) && (rmtp != NULL) &&
152 156 (uucopy(&rmt, rmtp, sizeof (struct timespec)) < 0))
153 157 return (-EFAULT);
154 158
155 159 return (ret);
↓ open down ↓ |
26 lines elided |
↑ open up ↑ |
156 160 }
157 161
158 162 /*ARGSUSED*/
159 163 long
160 164 lx_adjtimex(void *tp)
161 165 {
162 166 return (-EPERM);
163 167 }
164 168
165 169 /*
170 + * Notification function for use with native SIGEV_THREAD in order to
171 + * emulate Linux SIGEV_THREAD_ID. Native SIGEV_THREAD is used as the
172 + * timer mechanism and B_SIGEV_THREAD_ID performs the actual event
173 + * delivery to the appropriate lx tid.
174 + */
175 +static void
176 +lx_sigev_thread_id(union sigval sival)
177 +{
178 + lx_sigevent_t *lev = (lx_sigevent_t *)sival.sival_ptr;
179 + syscall(SYS_brand, B_SIGEV_THREAD_ID, lev->lx_sigev_un.lx_tid,
180 + lev->lx_sigev_signo, lev->lx_sigev_value.sival_ptr);
181 + free(lev);
182 +}
183 +
184 +
185 +/*
166 186 * The Illumos timer_create man page says it accepts the following clocks:
167 187 * CLOCK_REALTIME (3) wall clock
168 188 * CLOCK_VIRTUAL (1) user CPU usage clock - No Backend
169 189 * CLOCK_PROF (2) user and system CPU usage clock - No Backend
170 190 * CLOCK_HIGHRES (4) non-adjustable, high-resolution clock
171 191 * However, in reality the Illumos timer_create only accepts CLOCK_REALTIME
172 192 * and CLOCK_HIGHRES, and since we can't use CLOCK_HIGHRES in a zone, we're
173 193 * down to one clock.
174 194 */
175 195 long
176 196 lx_timer_create(int clock, struct sigevent *lx_sevp, timer_t *tid)
177 197 {
178 198 lx_sigevent_t lev;
179 199 struct sigevent sev;
180 200
181 201 if (clock < 0 || clock >= LX_TIMER_MAX)
↓ open down ↓ |
6 lines elided |
↑ open up ↑ |
182 202 return (-EINVAL);
183 203
184 204 /* We have to convert the Linux sigevent layout to the Illumos layout */
185 205 if (uucopy(lx_sevp, &lev, sizeof (lev)) < 0)
186 206 return (-EFAULT);
187 207
188 208 if (lev.lx_sigev_notify < 0 || lev.lx_sigev_notify > LX_SIGEV_MAX)
189 209 return (-EINVAL);
190 210
191 211 sev.sigev_notify = ltos_sigev[lev.lx_sigev_notify];
192 - sev.sigev_signo = ltos_signo[lev.lx_sigev_signo];
212 + sev.sigev_signo = lx_ltos_signo(lev.lx_sigev_signo, 0);
193 213 sev.sigev_value = lev.lx_sigev_value;
194 214
195 215 /*
196 - * The sigevent sigev_notify_function and sigev_notify_attributes
197 - * members are not used by timer_create, so no conversion is needed.
216 + * The signal number is meaningless in SIGEV_NONE, Linux
217 + * accepts any value. We convert invalid signals to 0 so other
218 + * parts of lx signal handling don't break.
198 219 */
220 + if ((sev.sigev_notify != SIGEV_NONE) && (sev.sigev_signo == 0))
221 + return (-EINVAL);
199 222
223 + /*
224 + * Assume all Linux libc implementations map SIGEV_THREAD to
225 + * SIGEV_THREAD_ID and ignore passed-in attributes.
226 + */
227 + sev.sigev_notify_attributes = NULL;
228 +
229 + if (lev.lx_sigev_notify == LX_SIGEV_THREAD_ID) {
230 + pid_t caller_pid = getpid();
231 + pid_t target_pid;
232 + lwpid_t ignore;
233 + lx_sigevent_t *lev_copy;
234 +
235 + if (lx_lpid_to_spair(lev.lx_sigev_un.lx_tid,
236 + &target_pid, &ignore) != 0)
237 + return (-EINVAL);
238 +
239 + /*
240 + * The caller of SIGEV_THREAD_ID must be in the same
241 + * process as the target thread.
242 + */
243 + if (caller_pid != target_pid)
244 + return (-EINVAL);
245 +
246 + /*
247 + * Pass the original lx sigevent_t to the native
248 + * notify function so that it may pass it to the lx
249 + * helper thread. It is the responsibility of
250 + * lx_sigev_thread_id() to free lev_copy after the
251 + * information is relayed to lx.
252 + *
253 + * If the calling process is forked without an exec
254 + * after this copy but before the timer fires then
255 + * lev_copy will leak in the child. This is acceptable
256 + * given the rarity of this event, the miniscule
257 + * amount leaked, and the fact that the memory is
258 + * reclaimed when the proc dies. It is firmly in the
259 + * land of "good enough".
260 + */
261 + lev_copy = malloc(sizeof (lx_sigevent_t));
262 + if (lev_copy == NULL)
263 + return (-ENOMEM);
264 +
265 + if (uucopy(&lev, lev_copy, sizeof (lx_sigevent_t)) < 0) {
266 + free(lev_copy);
267 + return (-EFAULT);
268 + }
269 +
270 + sev.sigev_notify_function = lx_sigev_thread_id;
271 + sev.sigev_value.sival_ptr = lev_copy;
272 + }
273 +
200 274 return ((timer_create(ltos_timer[clock], &sev, tid) < 0) ? -errno : 0);
201 275 }
202 276
203 277 long
204 278 lx_timer_settime(timer_t tid, int flags, struct itimerspec *new_val,
205 279 struct itimerspec *old_val)
206 280 {
207 281 return ((timer_settime(tid, flags, new_val, old_val) < 0) ? -errno : 0);
208 282 }
209 283
210 284 long
211 285 lx_timer_gettime(timer_t tid, struct itimerspec *val)
212 286 {
213 287 return ((timer_gettime(tid, val) < 0) ? -errno : 0);
214 288 }
215 289
216 290 long
217 291 lx_timer_getoverrun(timer_t tid)
218 292 {
219 293 int val;
220 294
221 295 val = timer_getoverrun(tid);
222 296 return ((val < 0) ? -errno : val);
223 297 }
224 298
225 299 long
226 300 lx_timer_delete(timer_t tid)
227 301 {
228 302 return ((timer_delete(tid) < 0) ? -errno : 0);
229 303 }
↓ open down ↓ |
20 lines elided |
↑ open up ↑ |
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX