summaryrefslogtreecommitdiff
path: root/usr/src
diff options
context:
space:
mode:
authorPatrick Mooney <pmooney@pfmooney.com>2016-11-17 20:44:48 +0000
committerPatrick Mooney <pmooney@pfmooney.com>2016-11-29 16:00:42 +0000
commit6bd01ddca2d0dd95b05bbc3db21df2e9bc2855b4 (patch)
tree6c829debd479a256748be20e843ac4d52cc02e3c /usr/src
parent12fff98ee5917359b18247a1cf2e8292ab958f8b (diff)
downloadillumos-joyent-6bd01ddca2d0dd95b05bbc3db21df2e9bc2855b4.tar.gz
OS-5792 lxbrand SIGEV_THREAD_ID timers inappropriately reuse allocation
Reviewed by: Ryan Zezeski <rpz@joyent.com> Reviewed by: Jerry Jelinek <jerry.jelinek@joyent.com> Approved by: Jerry Jelinek <jerry.jelinek@joyent.com>
Diffstat (limited to 'usr/src')
-rw-r--r--usr/src/lib/brand/lx/lx_brand/common/clock.c152
-rw-r--r--usr/src/lib/brand/lx/lx_brand/common/lx_brand.c4
-rw-r--r--usr/src/lib/brand/lx/lx_brand/sys/lx_syscall.h1
-rw-r--r--usr/src/uts/common/brand/lx/os/lx_brand.c56
-rw-r--r--usr/src/uts/common/brand/lx/os/lx_syscall.c4
-rw-r--r--usr/src/uts/common/brand/lx/sys/lx_brand.h2
-rw-r--r--usr/src/uts/common/brand/lx/sys/lx_syscalls.h1
-rw-r--r--usr/src/uts/common/brand/lx/syscall/lx_timer.c253
-rw-r--r--usr/src/uts/common/os/timer.c257
-rw-r--r--usr/src/uts/common/sys/timer.h26
10 files changed, 429 insertions, 327 deletions
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 4c7458e051..e627df68dc 100644
--- a/usr/src/lib/brand/lx/lx_brand/common/clock.c
+++ b/usr/src/lib/brand/lx/lx_brand/common/clock.c
@@ -26,12 +26,9 @@
*/
#include <errno.h>
-#include <stdlib.h>
#include <string.h>
#include <time.h>
-#include <unistd.h>
#include <sys/resource.h>
-#include <sys/syscall.h>
#include <sys/timerfd.h>
#include <sys/lx_misc.h>
#include <sys/lx_syscall.h>
@@ -84,34 +81,6 @@ static int ltos_clock[] = {
#define LX_CLOCK_MAX (sizeof (ltos_clock) / sizeof (ltos_clock[0]))
-#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 /* Linux SIGEV_THREAD_ID -- see lx_sigev_thread_id() */
-};
-
-#define LX_SIGEV_MAX (sizeof (ltos_sigev) / sizeof (ltos_sigev[0]))
-#define LX_SIGEV_THREAD_ID 4
long
lx_clock_nanosleep(int clock, int flags, struct timespec *rqtp,
@@ -157,127 +126,6 @@ lx_adjtimex(void *tp)
return (-EPERM);
}
-/*
- * Notification function for use with native SIGEV_THREAD in order to
- * emulate Linux SIGEV_THREAD_ID. Native SIGEV_THREAD is used as the
- * timer mechanism and B_SIGEV_THREAD_ID performs the actual event
- * delivery to the appropriate lx tid.
- */
-static void
-lx_sigev_thread_id(union sigval sival)
-{
- lx_sigevent_t *lev = (lx_sigevent_t *)sival.sival_ptr;
- (void) syscall(SYS_brand, B_SIGEV_THREAD_ID, lev->lx_sigev_un.lx_tid,
- lev->lx_sigev_signo, lev->lx_sigev_value.sival_ptr);
- free(lev);
-}
-
-
-/*
- * 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.
- *
- * Linux has complicated support for clock IDs. For example, the
- * clock_getcpuclockid() function can return a negative clock_id. See the Linux
- * source and the comment in include/linux/posix-timers.h (above CLOCKFD) which
- * describes clock file descriptors and shows how they map to a virt. or sched.
- * clock ID. A process can pass one of these negative IDs to timer_create so we
- * need to convert it and we currently only allow CLOCK_PROCESS_CPUTIME_ID
- * against the current process as the input.
- */
-long
-lx_timer_create(int clock, struct sigevent *lx_sevp, timer_t *tid)
-{
- lx_sigevent_t lev;
- struct sigevent sev;
-
- if (clock < 0) {
- if (clock != 0xfffffffe)
- return (-EINVAL);
- clock = CLOCK_RT_SLOT; /* force our use of CLOCK_REALTIME */
- }
-
- if (clock >= LX_CLOCK_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 = lx_ltos_signo(lev.lx_sigev_signo, 0);
- sev.sigev_value = lev.lx_sigev_value;
-
- /*
- * The signal number is meaningless in SIGEV_NONE, Linux
- * accepts any value. We convert invalid signals to 0 so other
- * parts of lx signal handling don't break.
- */
- if ((sev.sigev_notify != SIGEV_NONE) && (sev.sigev_signo == 0))
- return (-EINVAL);
-
- /*
- * Assume all Linux libc implementations map SIGEV_THREAD to
- * SIGEV_THREAD_ID and ignore passed-in attributes.
- */
- sev.sigev_notify_attributes = NULL;
-
- if (lev.lx_sigev_notify == LX_SIGEV_THREAD_ID) {
- pid_t caller_pid = getpid();
- pid_t target_pid;
- lwpid_t ignore;
- lx_sigevent_t *lev_copy;
-
- if (lx_lpid_to_spair(lev.lx_sigev_un.lx_tid,
- &target_pid, &ignore) != 0)
- return (-EINVAL);
-
- /*
- * The caller of SIGEV_THREAD_ID must be in the same
- * process as the target thread.
- */
- if (caller_pid != target_pid)
- return (-EINVAL);
-
- /*
- * Pass the original lx sigevent_t to the native
- * notify function so that it may pass it to the lx
- * helper thread. It is the responsibility of
- * lx_sigev_thread_id() to free lev_copy after the
- * information is relayed to lx.
- *
- * If the calling process is forked without an exec
- * after this copy but before the timer fires then
- * lev_copy will leak in the child. This is acceptable
- * given the rarity of this event, the miniscule
- * amount leaked, and the fact that the memory is
- * reclaimed when the proc dies. It is firmly in the
- * land of "good enough".
- */
- lev_copy = malloc(sizeof (lx_sigevent_t));
- if (lev_copy == NULL)
- return (-ENOMEM);
-
- if (uucopy(&lev, lev_copy, sizeof (lx_sigevent_t)) < 0) {
- free(lev_copy);
- return (-EFAULT);
- }
-
- sev.sigev_notify_function = lx_sigev_thread_id;
- sev.sigev_value.sival_ptr = lev_copy;
- }
-
- return ((timer_create(ltos_clock[clock], &sev, tid) < 0) ? -errno : 0);
-}
-
long
lx_timer_settime(timer_t tid, int flags, struct itimerspec *new_val,
struct itimerspec *old_val)
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 200df187ae..c027cfed5e 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
@@ -1233,7 +1233,7 @@ static lx_syscall_handler_t lx_handlers[] = {
NULL, /* 219: restart_syscall */
lx_semtimedop, /* 220: semtimedop */
NULL, /* 221: fadvise64 */
- lx_timer_create, /* 222: timer_create */
+ NULL, /* 222: timer_create */
lx_timer_settime, /* 223: timer_settime */
lx_timer_gettime, /* 224: timer_gettime */
lx_timer_getoverrun, /* 225: timer_getoverrun */
@@ -1601,7 +1601,7 @@ static lx_syscall_handler_t lx_handlers[] = {
NULL, /* 256: epoll_wait */
NULL, /* 257: remap_file_pages */
NULL, /* 258: set_tid_address */
- lx_timer_create, /* 259: timer_create */
+ NULL, /* 259: timer_create */
lx_timer_settime, /* 260: timer_settime */
lx_timer_gettime, /* 261: timer_gettime */
lx_timer_getoverrun, /* 262: timer_getoverrun */
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 efcbef8f70..e26ff7333c 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
@@ -71,7 +71,6 @@ extern long lx_capset(uintptr_t, uintptr_t);
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 *);
diff --git a/usr/src/uts/common/brand/lx/os/lx_brand.c b/usr/src/uts/common/brand/lx/os/lx_brand.c
index fa7da00684..33bab64751 100644
--- a/usr/src/uts/common/brand/lx/os/lx_brand.c
+++ b/usr/src/uts/common/brand/lx/os/lx_brand.c
@@ -1468,62 +1468,6 @@ lx_brandsys(int cmd, int64_t *rval, uintptr_t arg1, uintptr_t arg2,
return (0);
}
- case B_SIGEV_THREAD_ID: {
- /*
- * Emulate Linux's timer_create(2) SIGEV_THREAD_ID
- * notification method. This mechanism is only meant
- * for userland threading libraries such as glibc and
- * is documented as such. Therefore, assume this is
- * only ever invoked for the purpose of alerting a
- * Linux threading library. Assume that the tid is a
- * member of the caller's process and the signal
- * number is valid. See lx_sigev_thread_id() for the
- * userland side of this emulation.
- *
- * The return code from this function is not checked
- * by the caller since it executes in an asynchronous
- * context and there is nothing much to be done. If
- * this function does fail then it will manifest as
- * Linux threads waiting for a signal they will never
- * receive.
- *
- * arg1 -- Linux tid
- * arg2 -- Linux signal number
- * arg3 -- sigval pointer
- */
-
- int native_sig = lx_ltos_signo((int)arg2, 0);
- pid_t spid;
- int stid;
- sigqueue_t *sqp;
-
- if (native_sig == 0)
- return (EINVAL);
-
- if (lx_lpid_to_spair((pid_t)arg1, &spid, &stid) != 0) {
- return (ESRCH);
- }
- sqp = kmem_zalloc(sizeof (sigqueue_t), KM_SLEEP);
- mutex_enter(&curproc->p_lock);
- if ((t = idtot(curproc, stid)) == NULL) {
- mutex_exit(&curproc->p_lock);
- kmem_free(sqp, sizeof (sigqueue_t));
- return (ESRCH);
- }
-
- sqp->sq_info.si_signo = native_sig;
- sqp->sq_info.si_code = SI_TIMER;
- sqp->sq_info.si_pid = curproc->p_pid;
- sqp->sq_info.si_zoneid = getzoneid();
- sqp->sq_info.si_uid = crgetruid(CRED());
- sqp->sq_info.si_value.sival_ptr = (void *)arg3;
- sigaddqa(curproc, t, sqp);
-
- mutex_exit(&curproc->p_lock);
-
- return (0);
- }
-
case B_PTRACE_STOP_FOR_OPT:
return (lx_ptrace_stop_for_option((int)arg1, arg2 == 0 ?
B_FALSE : B_TRUE, (ulong_t)arg3, arg4));
diff --git a/usr/src/uts/common/brand/lx/os/lx_syscall.c b/usr/src/uts/common/brand/lx/os/lx_syscall.c
index 3a558d89b0..c8824e6783 100644
--- a/usr/src/uts/common/brand/lx/os/lx_syscall.c
+++ b/usr/src/uts/common/brand/lx/os/lx_syscall.c
@@ -778,7 +778,7 @@ lx_sysent_t lx_sysent32[] = {
{"epoll_wait", lx_epoll_wait, 0, 4}, /* 256 */
{"remap_file_pages", NULL, NOSYS_NO_EQUIV, 0}, /* 257 */
{"set_tid_address", lx_set_tid_address, 0, 1}, /* 258 */
- {"timer_create", NULL, 0, 3}, /* 259 */
+ {"timer_create", lx_timer_create, 0, 3}, /* 259 */
{"timer_settime", NULL, 0, 4}, /* 260 */
{"timer_gettime", NULL, 0, 2}, /* 261 */
{"timer_getoverrun", NULL, 0, 1}, /* 262 */
@@ -1112,7 +1112,7 @@ lx_sysent_t lx_sysent64[] = {
{"restart_syscall", NULL, NOSYS_NULL, 0}, /* 219 */
{"semtimedop", NULL, 0, 4}, /* 220 */
{"fadvise64", lx_fadvise64, 0, 4}, /* 221 */
- {"timer_create", NULL, 0, 3}, /* 222 */
+ {"timer_create", lx_timer_create, 0, 3}, /* 222 */
{"timer_settime", NULL, 0, 4}, /* 223 */
{"timer_gettime", NULL, 0, 2}, /* 224 */
{"timer_getoverrun", NULL, 0, 1}, /* 225 */
diff --git a/usr/src/uts/common/brand/lx/sys/lx_brand.h b/usr/src/uts/common/brand/lx/sys/lx_brand.h
index 1791170da6..30d576044f 100644
--- a/usr/src/uts/common/brand/lx/sys/lx_brand.h
+++ b/usr/src/uts/common/brand/lx/sys/lx_brand.h
@@ -109,7 +109,7 @@ extern "C" {
#define B_HELPER_SIGQUEUE 145
#define B_HELPER_TGSIGQUEUE 146
#define B_SET_NATIVE_STACK 147
-#define B_SIGEV_THREAD_ID 148
+/* formerly B_SIGEV_THREAD_ID 148 */
#define B_OVERRIDE_KERN_VER 149
#define B_PTRACE_SIG_RETURN 150
#define B_GET_PERSONALITY 151
diff --git a/usr/src/uts/common/brand/lx/sys/lx_syscalls.h b/usr/src/uts/common/brand/lx/sys/lx_syscalls.h
index 63afc0e795..2784ed6919 100644
--- a/usr/src/uts/common/brand/lx/sys/lx_syscalls.h
+++ b/usr/src/uts/common/brand/lx/sys/lx_syscalls.h
@@ -237,6 +237,7 @@ extern long lx_syslog();
extern long lx_removexattr();
extern long lx_tgkill();
extern long lx_time();
+extern long lx_timer_create();
extern long lx_tkill();
extern long lx_umask();
extern long lx_umount();
diff --git a/usr/src/uts/common/brand/lx/syscall/lx_timer.c b/usr/src/uts/common/brand/lx/syscall/lx_timer.c
index 17ca59b534..279bdbddc7 100644
--- a/usr/src/uts/common/brand/lx/syscall/lx_timer.c
+++ b/usr/src/uts/common/brand/lx/syscall/lx_timer.c
@@ -32,7 +32,10 @@
#include <sys/time.h>
#include <sys/systm.h>
#include <sys/cmn_err.h>
+#include <sys/brand.h>
+#include <sys/lx_brand.h>
#include <sys/lx_impl.h>
+#include <lx_signum.h>
/*
* From "uts/common/os/timer.c":
@@ -90,8 +93,64 @@ static lx_clock_backend_t lx_clock_backends[] = {
#define LX_CLOCK_MAX \
(sizeof (lx_clock_backends) / sizeof (lx_clock_backends[0]))
-#define LX_CLOCK_BACKEND(clk) \
- ((clk) < LX_CLOCK_MAX && (clk) >= 0 ? &lx_clock_backends[(clk)] : NULL)
+#define LX_CLOCK_BACKEND(clk) (((clk) < LX_CLOCK_MAX && (clk) >= 0) ? \
+ &lx_clock_backends[(clk)] : NULL)
+
+/*
+ * Linux defines the size of the sigevent structure to be 64 bytes. In order
+ * to meet that definition, the trailing union includes a member which pads it
+ * out to the desired length for the given architecture.
+ */
+#define LX_SIGEV_PAD_SIZE ((64 - \
+ (sizeof (int) * 2 + sizeof (union sigval))) / sizeof (int))
+
+typedef struct {
+ union sigval lx_sigev_value;
+ 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;
+
+
+#ifdef _SYSCALL32_IMPL
+
+#define LX_SIGEV32_PAD_SIZE ((64 - \
+ (sizeof (int) * 2 + sizeof (union sigval32))) / sizeof (int))
+
+typedef struct {
+ union sigval32 lx_sigev_value;
+ int lx_sigev_signo;
+ int lx_sigev_notify;
+ union {
+ int lx_pad[LX_SIGEV32_PAD_SIZE];
+ int lx_tid;
+ struct {
+ caddr32_t lx_notify_function;
+ caddr32_t lx_notify_attribute;
+ } lx_sigev_thread;
+ } lx_sigev_un;
+} lx_sigevent32_t;
+
+#endif /* _SYSCALL32_IMPL */
+
+#define LX_SIGEV_SIGNAL 0
+#define LX_SIGEV_NONE 1
+#define LX_SIGEV_THREAD 2
+#define LX_SIGEV_THREAD_ID 4
+
+/*
+ * Access private SIGEV_THREAD_ID callback state in itimer_t
+ */
+#define LX_SIGEV_THREAD_ID_LPID(it) ((it)->it_cb_data[0])
+#define LX_SIGEV_THREAD_ID_TID(it) ((it)->it_cb_data[1])
+
/* ARGSUSED */
static int
@@ -276,6 +335,196 @@ lx_clock_getres(int clock, timespec_t *tp)
return (backend->lclk_clock_getres(backend->lclk_ntv_id, tp));
}
+static int
+lx_ltos_sigev(lx_sigevent_t *lev, struct sigevent *sev)
+{
+ bzero(sev, sizeof (*sev));
+
+ switch (lev->lx_sigev_notify) {
+ case LX_SIGEV_NONE:
+ sev->sigev_notify = SIGEV_NONE;
+ break;
+
+ case LX_SIGEV_SIGNAL:
+ case LX_SIGEV_THREAD_ID:
+ sev->sigev_notify = SIGEV_SIGNAL;
+ break;
+
+ case LX_SIGEV_THREAD:
+ /*
+ * Just as in illumos, SIGEV_THREAD handling is performed in
+ * userspace with the help of SIGEV_SIGNAL/SIGEV_THREAD_ID.
+ *
+ * It's not expected to make an appearance in the syscall.
+ */
+ default:
+ return (EINVAL);
+ }
+
+ sev->sigev_signo = lx_ltos_signo(lev->lx_sigev_signo, 0);
+ sev->sigev_value = lev->lx_sigev_value;
+
+ /* Ensure SIGEV_SIGNAL has a valid signo to work with. */
+ if (sev->sigev_notify == SIGEV_SIGNAL && sev->sigev_signo == 0) {
+ return (EINVAL);
+ }
+ return (0);
+}
+
+static int
+lx_sigev_copyin(lx_sigevent_t *userp, lx_sigevent_t *levp)
+{
+#ifdef _SYSCALL32_IMPL
+ if (get_udatamodel() != DATAMODEL_NATIVE) {
+ lx_sigevent32_t lev32;
+
+ if (copyin(userp, &lev32, sizeof (lev32)) != 0) {
+ return (EFAULT);
+ }
+ levp->lx_sigev_value.sival_int = lev32.lx_sigev_value.sival_int;
+ levp->lx_sigev_signo = lev32.lx_sigev_signo;
+ levp->lx_sigev_notify = lev32.lx_sigev_notify;
+ levp->lx_sigev_un.lx_tid = lev32.lx_sigev_un.lx_tid;
+ } else
+#endif /* _SYSCALL32_IMPL */
+ {
+ if (copyin(userp, levp, sizeof (lx_sigevent_t)) != 0) {
+ return (EFAULT);
+ }
+ }
+ return (0);
+}
+
+static void
+lx_sigev_thread_fire(itimer_t *it)
+{
+ proc_t *p = it->it_proc;
+ pid_t lpid = (pid_t)LX_SIGEV_THREAD_ID_LPID(it);
+ id_t tid = (id_t)LX_SIGEV_THREAD_ID_TID(it);
+ lwpdir_t *ld;
+
+ ASSERT(MUTEX_HELD(&it->it_mutex));
+ ASSERT(it->it_pending == 0);
+ ASSERT(it->it_flags & IT_SIGNAL);
+ ASSERT(MUTEX_HELD(&p->p_lock));
+
+ ld = lwp_hash_lookup(p, tid);
+ if (ld != NULL) {
+ lx_lwp_data_t *lwpd;
+ kthread_t *t;
+
+ t = ld->ld_entry->le_thread;
+ lwpd = ttolxlwp(t);
+ if (lwpd != NULL && lwpd->br_pid == lpid) {
+ /*
+ * A thread matching the LX pid is still present in the
+ * process. Send a targeted signal as requested.
+ */
+ it->it_pending = 1;
+ mutex_exit(&it->it_mutex);
+ sigaddqa(p, t, it->it_sigq);
+ return;
+ }
+ }
+
+ mutex_exit(&it->it_mutex);
+}
+
+long
+lx_timer_create(int clock, lx_sigevent_t *sevp, timer_t *tidp)
+{
+ int error;
+ lx_sigevent_t lev;
+ struct sigevent sev;
+ clock_backend_t *backend = NULL;
+ proc_t *p = curproc;
+ itimer_t *itp;
+ timer_t tid;
+
+ if (clock == -2) {
+ /*
+ * A change was made to the old userspace timer emulation to
+ * handle this specific clock ID for MapR. It was wrongly
+ * mapped to CLOCK_REALTIME rather than CLOCK_THREAD_CPUTIME_ID
+ * which it maps to. Until the CLOCK_*_CPUTIME_ID timers can
+ * be emulated, the admittedly incorrect mapping will remain.
+ */
+ backend = clock_get_backend(CLOCK_REALTIME);
+ } else {
+ lx_clock_backend_t *lback = LX_CLOCK_BACKEND(clock);
+
+ if (lback != NULL) {
+ backend = clock_get_backend(lback->lclk_ntv_id);
+ }
+ }
+ if (backend == NULL) {
+ return (set_errno(EINVAL));
+ }
+
+ /* We have to convert the Linux sigevent layout to the illumos layout */
+ if (sevp != NULL) {
+ if ((error = lx_sigev_copyin(sevp, &lev)) != 0) {
+ return (set_errno(error));
+ }
+ if ((error = lx_ltos_sigev(&lev, &sev)) != 0) {
+ return (set_errno(error));
+ }
+ } else {
+ bzero(&sev, sizeof (sev));
+ sev.sigev_notify = SIGEV_SIGNAL;
+ sev.sigev_signo = SIGALRM;
+ }
+
+ if ((error = timer_setup(backend, &sev, NULL, &itp, &tid)) != 0) {
+ return (set_errno(error));
+ }
+
+ /*
+ * The SIGEV_THREAD_ID notification method in Linux allows the caller
+ * to target a specific thread to receive the signal. The IT_CALLBACK
+ * timer functionality is used to fulfill this need. After translating
+ * the LX pid to a SunOS thread ID (ensuring it exists in the current
+ * process), those IDs are attached to the timer along with the custom
+ * lx_sigev_thread_fire callback. This targets the signal notification
+ * properly when the timer fires.
+ */
+ if (lev.lx_sigev_notify == LX_SIGEV_THREAD_ID) {
+ pid_t lpid, spid;
+ id_t stid;
+
+ lpid = (pid_t)lev.lx_sigev_un.lx_tid;
+ if (lx_lpid_to_spair(lpid, &spid, &stid) != 0 ||
+ spid != curproc->p_pid) {
+ error = EINVAL;
+ goto err;
+ }
+
+ itp->it_flags |= IT_CALLBACK;
+ itp->it_cb_func = lx_sigev_thread_fire;
+ LX_SIGEV_THREAD_ID_LPID(itp) = lpid;
+ LX_SIGEV_THREAD_ID_TID(itp) = stid;
+ }
+
+ /*
+ * When the sigevent is not specified, its sigev_value field is
+ * expected to be populated with the timer ID.
+ */
+ if (sevp == NULL) {
+ itp->it_sigq->sq_info.si_value.sival_int = tid;
+ }
+
+ if (copyout(&tid, tidp, sizeof (timer_t)) != 0) {
+ error = EFAULT;
+ goto err;
+ }
+
+ timer_release(p, itp);
+ return (0);
+
+err:
+ timer_delete_grabbed(p, tid, itp);
+ return (set_errno(error));
+}
long
lx_gettimeofday(struct timeval *tvp, struct lx_timezone *tzp)
diff --git a/usr/src/uts/common/os/timer.c b/usr/src/uts/common/os/timer.c
index 845b93da6e..e87e759563 100644
--- a/usr/src/uts/common/os/timer.c
+++ b/usr/src/uts/common/os/timer.c
@@ -140,7 +140,7 @@ timer_delete_locked(proc_t *p, timer_t tid, itimer_t *it)
it->it_backend->clk_timer_delete(it);
- if (it->it_portev) {
+ if (it->it_flags & IT_PORT) {
mutex_enter(&it->it_mutex);
if (it->it_portev) {
port_kevent_t *pev;
@@ -237,7 +237,7 @@ timer_grab(proc_t *p, timer_t tid)
* should not be held on entry; timer_release() will acquire p_lock but
* will drop it before returning.
*/
-static void
+void
timer_release(proc_t *p, itimer_t *it)
{
mutex_enter(&p->p_lock);
@@ -250,7 +250,7 @@ timer_release(proc_t *p, itimer_t *it)
* p_lock should not be held on entry; timer_delete_grabbed() will acquire
* p_lock, but will drop it before returning.
*/
-static void
+void
timer_delete_grabbed(proc_t *p, timer_t tid, itimer_t *it)
{
mutex_enter(&p->p_lock);
@@ -465,6 +465,9 @@ timer_fire(itimer_t *it)
it->it_pending = 1;
port_send_event((port_kevent_t *)it->it_portev);
mutex_exit(&it->it_mutex);
+ } else if (it->it_flags & IT_CALLBACK) {
+ it->it_cb_func(it);
+ ASSERT(MUTEX_NOT_HELD(&it->it_mutex));
} else if (it->it_flags & IT_SIGNAL) {
it->it_pending = 1;
mutex_exit(&it->it_mutex);
@@ -580,85 +583,27 @@ retry:
return (it);
}
+/*
+ * Setup a timer
+ *
+ * This allocates an itimer_t (including a timer_t ID and slot in the process),
+ * wires it up according to the provided sigevent, and associates it with the
+ * desired clock backend. Upon successful completion, the timer will be
+ * locked, preventing it from being armed via timer_settime() or deleted via
+ * timer_delete(). This gives the caller a chance to perform any last minute
+ * manipulations (such as configuring the IT_CALLBACK functionality and/or
+ * copying the timer_t out to userspace) before using timer_release() to unlock
+ * it or timer_delete_grabbed() to delete it.
+ */
int
-timer_create(clockid_t clock, struct sigevent *evp, timer_t *tid)
+timer_setup(clock_backend_t *backend, struct sigevent *evp, port_notify_t *pnp,
+ itimer_t **itp, timer_t *tidp)
{
- struct sigevent ev;
proc_t *p = curproc;
- clock_backend_t *backend;
+ int error = 0;
itimer_t *it;
sigqueue_t *sigq;
- cred_t *cr = CRED();
- int error = 0;
- timer_t i;
- port_notify_t tim_pnevp;
- port_kevent_t *pkevp = NULL;
-
- if ((backend = CLOCK_BACKEND(clock)) == NULL)
- return (set_errno(EINVAL));
-
- if (evp != NULL) {
- /*
- * short copyin() for binary compatibility
- * fetch oldsigevent to determine how much to copy in.
- */
- if (get_udatamodel() == DATAMODEL_NATIVE) {
- if (copyin(evp, &ev, sizeof (struct oldsigevent)))
- return (set_errno(EFAULT));
-
- if (ev.sigev_notify == SIGEV_PORT ||
- ev.sigev_notify == SIGEV_THREAD) {
- if (copyin(ev.sigev_value.sival_ptr, &tim_pnevp,
- sizeof (port_notify_t)))
- return (set_errno(EFAULT));
- }
-#ifdef _SYSCALL32_IMPL
- } else {
- struct sigevent32 ev32;
- port_notify32_t tim_pnevp32;
-
- if (copyin(evp, &ev32, sizeof (struct oldsigevent32)))
- return (set_errno(EFAULT));
- ev.sigev_notify = ev32.sigev_notify;
- ev.sigev_signo = ev32.sigev_signo;
- /*
- * See comment in sigqueue32() on handling of 32-bit
- * sigvals in a 64-bit kernel.
- */
- ev.sigev_value.sival_int = ev32.sigev_value.sival_int;
- if (ev.sigev_notify == SIGEV_PORT ||
- ev.sigev_notify == SIGEV_THREAD) {
- if (copyin((void *)(uintptr_t)
- ev32.sigev_value.sival_ptr,
- (void *)&tim_pnevp32,
- sizeof (port_notify32_t)))
- return (set_errno(EFAULT));
- tim_pnevp.portnfy_port =
- tim_pnevp32.portnfy_port;
- tim_pnevp.portnfy_user =
- (void *)(uintptr_t)tim_pnevp32.portnfy_user;
- }
-#endif
- }
- switch (ev.sigev_notify) {
- case SIGEV_NONE:
- break;
- case SIGEV_SIGNAL:
- if (ev.sigev_signo < 1 || ev.sigev_signo >= NSIG)
- return (set_errno(EINVAL));
- break;
- case SIGEV_THREAD:
- case SIGEV_PORT:
- break;
- default:
- return (set_errno(EINVAL));
- }
- } else {
- /*
- * Use the clock's default sigevent (this is a structure copy).
- */
- ev = backend->clk_default;
- }
+ timer_t tid;
/*
* We'll allocate our sigqueue now, before we grab p_lock.
@@ -669,31 +614,29 @@ timer_create(clockid_t clock, struct sigevent *evp, timer_t *tid)
/*
* Allocate a timer and choose a slot for it. This acquires p_lock.
*/
- it = timer_alloc(p, &i);
+ it = timer_alloc(p, &tid);
ASSERT(MUTEX_HELD(&p->p_lock));
if (it == NULL) {
mutex_exit(&p->p_lock);
kmem_free(sigq, sizeof (sigqueue_t));
- return (set_errno(EAGAIN));
+ return (EAGAIN);
}
- ASSERT(i < p->p_itimer_sz && p->p_itimer[i] == NULL);
+ ASSERT(tid < p->p_itimer_sz && p->p_itimer[tid] == NULL);
+ ASSERT(evp != NULL);
/*
* If we develop other notification mechanisms, this will need
* to call into (yet another) backend.
*/
- sigq->sq_info.si_signo = ev.sigev_signo;
- if (evp == NULL)
- sigq->sq_info.si_value.sival_int = i;
- else
- sigq->sq_info.si_value = ev.sigev_value;
+ sigq->sq_info.si_signo = evp->sigev_signo;
+ sigq->sq_info.si_value = evp->sigev_value;
sigq->sq_info.si_code = SI_TIMER;
sigq->sq_info.si_pid = p->p_pid;
sigq->sq_info.si_ctid = PRCTID(p);
sigq->sq_info.si_zoneid = getzoneid();
- sigq->sq_info.si_uid = crgetruid(cr);
+ sigq->sq_info.si_uid = crgetruid(CRED());
sigq->sq_func = timer_signal;
sigq->sq_next = NULL;
sigq->sq_backptr = it;
@@ -701,9 +644,12 @@ timer_create(clockid_t clock, struct sigevent *evp, timer_t *tid)
it->it_backend = backend;
it->it_lock = ITLK_LOCKED;
- if (ev.sigev_notify == SIGEV_THREAD ||
- ev.sigev_notify == SIGEV_PORT) {
+ if (evp->sigev_notify == SIGEV_THREAD ||
+ evp->sigev_notify == SIGEV_PORT) {
int port;
+ port_kevent_t *pkevp = NULL;
+
+ ASSERT(pnp != NULL);
/*
* This timer is programmed to use event port notification when
@@ -723,7 +669,7 @@ timer_create(clockid_t clock, struct sigevent *evp, timer_t *tid)
*/
it->it_flags |= IT_PORT;
- port = tim_pnevp.portnfy_port;
+ port = pnp->portnfy_port;
/* associate timer as event source with the port */
error = port_associate_ksource(port, PORT_SOURCE_TIMER,
@@ -733,7 +679,7 @@ timer_create(clockid_t clock, struct sigevent *evp, timer_t *tid)
mutex_exit(&p->p_lock);
kmem_cache_free(clock_timer_cache, it);
kmem_free(sigq, sizeof (sigqueue_t));
- return (set_errno(error));
+ return (error);
}
/* allocate an event structure/slot */
@@ -745,21 +691,21 @@ timer_create(clockid_t clock, struct sigevent *evp, timer_t *tid)
mutex_exit(&p->p_lock);
kmem_cache_free(clock_timer_cache, it);
kmem_free(sigq, sizeof (sigqueue_t));
- return (set_errno(error));
+ return (error);
}
/* initialize event data */
- port_init_event(pkevp, i, tim_pnevp.portnfy_user,
+ port_init_event(pkevp, tid, pnp->portnfy_user,
timer_port_callback, it);
it->it_portev = pkevp;
it->it_portfd = port;
} else {
- if (ev.sigev_notify == SIGEV_SIGNAL)
+ if (evp->sigev_notify == SIGEV_SIGNAL)
it->it_flags |= IT_SIGNAL;
}
/* Populate the slot now that the timer is prepped. */
- p->p_itimer[i] = it;
+ p->p_itimer[tid] = it;
mutex_exit(&p->p_lock);
/*
@@ -772,17 +718,8 @@ timer_create(clockid_t clock, struct sigevent *evp, timer_t *tid)
it->it_lwp = ttolwp(curthread);
it->it_proc = p;
- if (copyout(&i, tid, sizeof (timer_t)) != 0) {
- error = EFAULT;
- goto err;
- }
-
- /*
- * If we're here, then we have successfully created the timer; we
- * just need to release the timer and return.
- */
- timer_release(p, it);
-
+ *itp = it;
+ *tidp = tid;
return (0);
err:
@@ -793,11 +730,115 @@ err:
* impossible for a removal to be pending.
*/
ASSERT(!(it->it_lock & ITLK_REMOVE));
- timer_delete_grabbed(p, i, it);
+ timer_delete_grabbed(p, tid, it);
+
+ return (error);
+}
+
- return (set_errno(error));
+int
+timer_create(clockid_t clock, struct sigevent *evp, timer_t *tidp)
+{
+ int error = 0;
+ proc_t *p = curproc;
+ clock_backend_t *backend;
+ struct sigevent ev;
+ itimer_t *it;
+ timer_t tid;
+ port_notify_t tim_pnevp;
+
+ if ((backend = CLOCK_BACKEND(clock)) == NULL)
+ return (set_errno(EINVAL));
+
+ if (evp != NULL) {
+ /*
+ * short copyin() for binary compatibility
+ * fetch oldsigevent to determine how much to copy in.
+ */
+ if (get_udatamodel() == DATAMODEL_NATIVE) {
+ if (copyin(evp, &ev, sizeof (struct oldsigevent)))
+ return (set_errno(EFAULT));
+
+ if (ev.sigev_notify == SIGEV_PORT ||
+ ev.sigev_notify == SIGEV_THREAD) {
+ if (copyin(ev.sigev_value.sival_ptr, &tim_pnevp,
+ sizeof (port_notify_t)))
+ return (set_errno(EFAULT));
+ }
+#ifdef _SYSCALL32_IMPL
+ } else {
+ struct sigevent32 ev32;
+ port_notify32_t tim_pnevp32;
+
+ if (copyin(evp, &ev32, sizeof (struct oldsigevent32)))
+ return (set_errno(EFAULT));
+ ev.sigev_notify = ev32.sigev_notify;
+ ev.sigev_signo = ev32.sigev_signo;
+ /*
+ * See comment in sigqueue32() on handling of 32-bit
+ * sigvals in a 64-bit kernel.
+ */
+ ev.sigev_value.sival_int = ev32.sigev_value.sival_int;
+ if (ev.sigev_notify == SIGEV_PORT ||
+ ev.sigev_notify == SIGEV_THREAD) {
+ if (copyin((void *)(uintptr_t)
+ ev32.sigev_value.sival_ptr,
+ (void *)&tim_pnevp32,
+ sizeof (port_notify32_t)))
+ return (set_errno(EFAULT));
+ tim_pnevp.portnfy_port =
+ tim_pnevp32.portnfy_port;
+ tim_pnevp.portnfy_user =
+ (void *)(uintptr_t)tim_pnevp32.portnfy_user;
+ }
+#endif
+ }
+ switch (ev.sigev_notify) {
+ case SIGEV_NONE:
+ break;
+ case SIGEV_SIGNAL:
+ if (ev.sigev_signo < 1 || ev.sigev_signo >= NSIG)
+ return (set_errno(EINVAL));
+ break;
+ case SIGEV_THREAD:
+ case SIGEV_PORT:
+ break;
+ default:
+ return (set_errno(EINVAL));
+ }
+ } else {
+ /*
+ * Use the clock's default sigevent (this is a structure copy).
+ */
+ ev = backend->clk_default;
+ }
+
+ if ((error = timer_setup(backend, &ev, &tim_pnevp, &it, &tid)) != 0) {
+ return (set_errno(error));
+ }
+
+ /*
+ * Populate si_value with the timer ID if no sigevent was passed in.
+ */
+ if (evp == NULL) {
+ it->it_sigq->sq_info.si_value.sival_int = tid;
+ }
+
+ if (copyout(&tid, tidp, sizeof (timer_t)) != 0) {
+ timer_delete_grabbed(p, tid, it);
+ return (set_errno(EFAULT));
+ }
+
+ /*
+ * If we're here, then we have successfully created the timer; we
+ * just need to release the timer and return.
+ */
+ timer_release(p, it);
+
+ return (0);
}
+
int
timer_gettime(timer_t tid, itimerspec_t *val)
{
@@ -1065,7 +1106,7 @@ timer_close_port(void *arg, int port, pid_t pid, int lastclose)
for (tid = 0; tid < timer_max; tid++) {
if ((it = timer_grab(p, tid)) == NULL)
continue;
- if (it->it_portev) {
+ if (it->it_flags & IT_PORT) {
mutex_enter(&it->it_mutex);
if (it->it_portfd == port) {
port_kevent_t *pev;
diff --git a/usr/src/uts/common/sys/timer.h b/usr/src/uts/common/sys/timer.h
index 688a381ecc..748e0c0627 100644
--- a/usr/src/uts/common/sys/timer.h
+++ b/usr/src/uts/common/sys/timer.h
@@ -35,6 +35,8 @@
#include <sys/proc.h>
#include <sys/thread.h>
#include <sys/param.h>
+#include <sys/siginfo.h>
+#include <sys/port.h>
#ifdef __cplusplus
extern "C" {
@@ -63,6 +65,7 @@ extern int timer_max;
*/
#define IT_SIGNAL 0x01
#define IT_PORT 0x02 /* use event port notification */
+#define IT_CALLBACK 0x04 /* custom callback function */
struct clock_backend;
@@ -90,14 +93,27 @@ struct itimer {
struct clock_backend *it_backend;
void (*it_fire)(itimer_t *);
kmutex_t it_mutex;
- void *it_portev; /* port_kevent_t pointer */
- void *it_portsrc; /* port_source_t pointer */
- int it_portfd; /* port file descriptor */
+ union {
+ struct {
+ void *_it_portev; /* port_kevent_t pointer */
+ void *_it_portsrc; /* port_source_t pointer */
+ int _it_portfd; /* port file descriptor */
+ } _it_ev_port;
+ struct {
+ void (*_it_cb_func)(itimer_t *);
+ uintptr_t _it_cb_data[2];
+ } _it_ev_cb;
+ } _it_ev_data;
};
#define it_sigq __data.__proc.__it_sigq
#define it_lwp __data.__proc.__it_lwp
#define it_frontend __data.__it_frontend
+#define it_portev _it_ev_data._it_ev_port._it_portev
+#define it_portsrc _it_ev_data._it_ev_port._it_portsrc
+#define it_portfd _it_ev_data._it_ev_port._it_portfd
+#define it_cb_func _it_ev_data._it_ev_cb._it_cb_func
+#define it_cb_data _it_ev_data._it_ev_cb._it_cb_data
typedef struct clock_backend {
struct sigevent clk_default;
@@ -114,7 +130,11 @@ typedef struct clock_backend {
extern void clock_add_backend(clockid_t clock, clock_backend_t *backend);
extern clock_backend_t *clock_get_backend(clockid_t clock);
+extern void timer_release(struct proc *, itimer_t *);
+extern void timer_delete_grabbed(struct proc *, timer_t tid, itimer_t *it);
extern void timer_lwpbind();
+extern int timer_setup(clock_backend_t *, struct sigevent *, port_notify_t *,
+ itimer_t **, timer_t *);
extern void timer_func(sigqueue_t *);
extern void timer_exit(void);