diff options
| author | Patrick Mooney <pmooney@pfmooney.com> | 2016-11-17 20:44:48 +0000 |
|---|---|---|
| committer | Patrick Mooney <pmooney@pfmooney.com> | 2016-11-29 16:00:42 +0000 |
| commit | 6bd01ddca2d0dd95b05bbc3db21df2e9bc2855b4 (patch) | |
| tree | 6c829debd479a256748be20e843ac4d52cc02e3c /usr/src | |
| parent | 12fff98ee5917359b18247a1cf2e8292ab958f8b (diff) | |
| download | illumos-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.c | 152 | ||||
| -rw-r--r-- | usr/src/lib/brand/lx/lx_brand/common/lx_brand.c | 4 | ||||
| -rw-r--r-- | usr/src/lib/brand/lx/lx_brand/sys/lx_syscall.h | 1 | ||||
| -rw-r--r-- | usr/src/uts/common/brand/lx/os/lx_brand.c | 56 | ||||
| -rw-r--r-- | usr/src/uts/common/brand/lx/os/lx_syscall.c | 4 | ||||
| -rw-r--r-- | usr/src/uts/common/brand/lx/sys/lx_brand.h | 2 | ||||
| -rw-r--r-- | usr/src/uts/common/brand/lx/sys/lx_syscalls.h | 1 | ||||
| -rw-r--r-- | usr/src/uts/common/brand/lx/syscall/lx_timer.c | 253 | ||||
| -rw-r--r-- | usr/src/uts/common/os/timer.c | 257 | ||||
| -rw-r--r-- | usr/src/uts/common/sys/timer.h | 26 |
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); |
