From 56c06c4b674b7f19af6b1aa9960966d3b82dd245 Mon Sep 17 00:00:00 2001 From: David Xu Date: Sun, 30 Oct 2005 02:56:08 +0000 Subject: [PATCH] Let itimer store itimerspec instead of itimerval, so I don't have to convert to or from timeval frequently. Introduce function itimer_accept() to ack a timer signal in signal acceptance code, this allows us to return more fresh overrun counter than at signal generating time. while POSIX says: "the value returned by timer_getoverrun() shall apply to the most recent expiration signal delivery or acceptance for the timer,.." I prefer returning it at acceptance time. Introduce SIGEV_THREAD_ID notification mode, it is used by thread libary to request kernel to deliver signal to a specified thread, and in turn, the thread library may use the mechanism to implement SIGEV_THREAD which is required by POSIX. Timer signal is managed by timer code, so it can not fail even if signal queue is full filled by sigqueue syscall. --- sys/kern/kern_sig.c | 6 +- sys/kern/kern_time.c | 177 ++++++++++++++++++++++++++++--------------- sys/sys/timers.h | 4 +- 3 files changed, 125 insertions(+), 62 deletions(-) diff --git a/sys/kern/kern_sig.c b/sys/kern/kern_sig.c index 169e2c7a1525..d63affa1c52e 100644 --- a/sys/kern/kern_sig.c +++ b/sys/kern/kern_sig.c @@ -69,6 +69,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include @@ -1252,6 +1253,8 @@ kern_sigtimedwait(struct thread *td, sigset_t waitset, ksiginfo_t *ksi, ksiginfo_init(ksi); sigqueue_get(&td->td_sigqueue, sig, ksi); ksi->ksi_signo = sig; + if (ksi->ksi_code == SI_TIMER) + itimer_accept(p, ksi->ksi_timerid, ksi); error = 0; mtx_lock(&ps->ps_mtx); action = ps->ps_sigact[_SIG_IDX(sig)]; @@ -2670,7 +2673,8 @@ postsig(sig) ksiginfo_init(&ksi); sigqueue_get(&td->td_sigqueue, sig, &ksi); ksi.ksi_signo = sig; - + if (ksi.ksi_code == SI_TIMER) + itimer_accept(p, ksi.ksi_timerid, &ksi); action = ps->ps_sigact[_SIG_IDX(sig)]; #ifdef KTRACE if (KTRPOINT(td, KTR_PSIG)) diff --git a/sys/kern/kern_time.c b/sys/kern/kern_time.c index 3305e9ada092..7d3ac1ad68f2 100644 --- a/sys/kern/kern_time.c +++ b/sys/kern/kern_time.c @@ -88,7 +88,7 @@ static int realtimer_gettime(struct itimer *, struct itimerspec *); static int realtimer_settime(struct itimer *, int, struct itimerspec *, struct itimerspec *); static int realtimer_delete(struct itimer *); -static void realtimer_clocktime(clockid_t, struct timeval *); +static void realtimer_clocktime(clockid_t, struct timespec *); static void realtimer_expire(void *); static void realtimer_event_hook(struct proc *, clockid_t, int event); static int kern_timer_create(struct thread *, clockid_t, @@ -97,6 +97,7 @@ static int kern_timer_delete(struct thread *, timer_t); int register_posix_clock(int, struct kclock *); void itimer_fire(struct itimer *it); +int itimespecfix(struct timespec *ts); #define CLOCK_CALL(clock, call, arglist) \ ((*posix_clocks[clock].call) arglist) @@ -957,9 +958,11 @@ kern_timer_create(struct thread *td, clockid_t clock_id, if (evp != NULL) { if (evp->sigev_notify != SIGEV_NONE && - evp->sigev_notify != SIGEV_SIGNAL) + evp->sigev_notify != SIGEV_SIGNAL && + evp->sigev_notify != SIGEV_THREAD_ID) return (EINVAL); - if (evp->sigev_notify == SIGEV_SIGNAL && + if ((evp->sigev_notify == SIGEV_SIGNAL || + evp->sigev_notify == SIGEV_THREAD_ID) && !_SIG_VALID(evp->sigev_signo)) return (EINVAL); } @@ -971,8 +974,8 @@ kern_timer_create(struct thread *td, clockid_t clock_id, it->it_flags = 0; it->it_usecount = 0; it->it_active = 0; - timevalclear(&it->it_time.it_value); - timevalclear(&it->it_time.it_interval); + timespecclear(&it->it_time.it_value); + timespecclear(&it->it_time.it_interval); it->it_overrun = 0; it->it_overrun_last = 0; it->it_clockid = clock_id; @@ -1028,7 +1031,8 @@ kern_timer_create(struct thread *td, clockid_t clock_id, it->it_sigev.sigev_value.sigval_int = id; } - if (it->it_sigev.sigev_notify == SIGEV_SIGNAL) { + if (it->it_sigev.sigev_notify == SIGEV_SIGNAL || + it->it_sigev.sigev_notify == SIGEV_THREAD_ID) { it->it_ksi.ksi_signo = it->it_sigev.sigev_signo; it->it_ksi.ksi_code = SI_TIMER; it->it_ksi.ksi_value = it->it_sigev.sigev_value; @@ -1204,9 +1208,9 @@ timer_getoverrun(struct thread *td, struct timer_getoverrun_args *uap) PROC_UNLOCK(p); error = EINVAL; } else { - PROC_UNLOCK(p); td->td_retval[0] = it->it_overrun_last; ITIMER_UNLOCK(it); + PROC_UNLOCK(p); error = 0; } return (error); @@ -1230,18 +1234,14 @@ realtimer_delete(struct itimer *it) static int realtimer_gettime(struct itimer *it, struct itimerspec *ovalue) { - struct timespec ts; + struct timespec cts; mtx_assert(&it->it_mtx, MA_OWNED); - TIMEVAL_TO_TIMESPEC(&it->it_time.it_value, &ovalue->it_value); - TIMEVAL_TO_TIMESPEC(&it->it_time.it_interval, &ovalue->it_interval); - if (it->it_clockid == CLOCK_REALTIME) - getnanotime(&ts); - else /* CLOCK_MONOTONIC */ - getnanouptime(&ts); + realtimer_clocktime(it->it_clockid, &cts); + *ovalue = it->it_time; if (ovalue->it_value.tv_sec != 0 || ovalue->it_value.tv_nsec != 0) { - timespecsub(&ovalue->it_value, &ts); + timespecsub(&ovalue->it_value, &cts); if (ovalue->it_value.tv_sec < 0 || (ovalue->it_value.tv_sec == 0 && ovalue->it_value.tv_nsec == 0)) { @@ -1256,42 +1256,42 @@ static int realtimer_settime(struct itimer *it, int flags, struct itimerspec *value, struct itimerspec *ovalue) { - struct timeval tv, tv2; - struct itimerval val; + struct timespec cts, ts; + struct timeval tv; + struct itimerspec val; mtx_assert(&it->it_mtx, MA_OWNED); - TIMESPEC_TO_TIMEVAL(&val.it_value, &value->it_value); - TIMESPEC_TO_TIMEVAL(&val.it_interval, &value->it_interval); - - if (itimerfix(&val.it_value)) + val = *value; + if (itimespecfix(&val.it_value)) return (EINVAL); - if (timevalisset(&val.it_value)) { - if (itimerfix(&val.it_interval)) + if (timespecisset(&val.it_value)) { + if (itimespecfix(&val.it_interval)) return (EINVAL); } else { - timevalclear(&val.it_interval); + timespecclear(&val.it_interval); } if (ovalue != NULL) realtimer_gettime(it, ovalue); it->it_time = val; - if (timevalisset(&val.it_value)) { - realtimer_clocktime(it->it_clockid, &tv); - tv2 = val.it_value; + if (timespecisset(&val.it_value)) { + realtimer_clocktime(it->it_clockid, &cts); + ts = val.it_value; if ((flags & TIMER_ABSTIME) == 0) { /* Convert to absolute time. */ - timevaladd(&it->it_time.it_value, &tv); + timespecadd(&it->it_time.it_value, &cts); } else { - timevalsub(&tv2, &tv); + timespecsub(&ts, &cts); /* - * We don't care if tv2 is negative, tztohz will + * We don't care if ts is negative, tztohz will * fix it. */ } - callout_reset(&it->it_callout, tvtohz(&tv2), + TIMESPEC_TO_TIMEVAL(&tv, &ts); + callout_reset(&it->it_callout, tvtohz(&tv), realtimer_expire, it); } else { callout_stop(&it->it_callout); @@ -1301,12 +1301,40 @@ realtimer_settime(struct itimer *it, int flags, } static void -realtimer_clocktime(clockid_t id, struct timeval *tv) +realtimer_clocktime(clockid_t id, struct timespec *ts) { if (id == CLOCK_REALTIME) - getmicrotime(tv); + getnanotime(ts); else /* CLOCK_MONOTONIC */ - getmicrouptime(tv); + getnanouptime(ts); +} + +int +itimer_accept(struct proc *p, timer_t timerid, ksiginfo_t *ksi) +{ + struct itimer *it; + + PROC_LOCK_ASSERT(p, MA_OWNED); + it = itimer_find(p, timerid, 0); + if (it != NULL) { + ksi->ksi_overrun = it->it_overrun; + it->it_overrun_last = it->it_overrun; + it->it_overrun = 0; + ITIMER_UNLOCK(it); + return (0); + } + return (EINVAL); +} + +int +itimespecfix(struct timespec *ts) +{ + + if (ts->tv_sec < 0 || ts->tv_nsec < 0 || ts->tv_nsec >= 1000000000) + return (EINVAL); + if (ts->tv_sec == 0 && ts->tv_nsec != 0 && ts->tv_nsec < tick * 1000) + ts->tv_nsec = tick * 1000; + return (0); } static void @@ -1339,41 +1367,44 @@ realtimer_event_hook(struct proc *p, clockid_t clock_id, int event) static void realtimer_expire(void *arg) { - struct timeval tv, tv2; + struct timespec cts, ts; + struct timeval tv; struct itimer *it; struct proc *p; it = (struct itimer *)arg; p = it->it_proc; - realtimer_clocktime(it->it_clockid, &tv); + realtimer_clocktime(it->it_clockid, &cts); /* Only fire if time is reached. */ - if (timevalcmp(&it->it_time.it_value, &tv, <=)) { - if (timevalisset(&it->it_time.it_interval)) { - timevaladd(&it->it_time.it_value, - &it->it_time.it_interval); - while (timevalcmp(&it->it_time.it_value, &tv, <=)) { + if (timespeccmp(&cts, &it->it_time.it_value, >=)) { + if (timespecisset(&it->it_time.it_interval)) { + timespecadd(&it->it_time.it_value, + &it->it_time.it_interval); + while (timespeccmp(&cts, &it->it_time.it_value, >=)) { it->it_overrun++; - timevaladd(&it->it_time.it_value, - &it->it_time.it_interval); + timespecadd(&it->it_time.it_value, + &it->it_time.it_interval); } } else { /* single shot timer ? */ - timevalclear(&it->it_time.it_value); + timespecclear(&it->it_time.it_value); } - if (timevalisset(&it->it_time.it_value)) { - tv2 = it->it_time.it_value; - timevalsub(&tv2, &tv); - callout_reset(&it->it_callout, tvtohz(&tv2), + if (timespecisset(&it->it_time.it_value)) { + ts = it->it_time.it_value; + timespecsub(&ts, &cts); + TIMESPEC_TO_TIMEVAL(&tv, &ts); + callout_reset(&it->it_callout, tvtohz(&tv), realtimer_expire, it); } ITIMER_UNLOCK(it); itimer_fire(it); ITIMER_LOCK(it); - } else if (timevalisset(&it->it_time.it_value)) { - tv2 = it->it_time.it_value; - timevalsub(&tv2, &tv); - callout_reset(&it->it_callout, tvtohz(&tv2), realtimer_expire, + } else if (timespecisset(&it->it_time.it_value)) { + ts = it->it_time.it_value; + timespecsub(&ts, &cts); + TIMESPEC_TO_TIMEVAL(&tv, &ts); + callout_reset(&it->it_callout, tvtohz(&tv), realtimer_expire, it); } } @@ -1382,20 +1413,48 @@ void itimer_fire(struct itimer *it) { struct proc *p = it->it_proc; + struct thread *td; - if (it->it_sigev.sigev_notify == SIGEV_SIGNAL) { + if (it->it_sigev.sigev_notify == SIGEV_SIGNAL || + it->it_sigev.sigev_notify == SIGEV_THREAD_ID) { PROC_LOCK(p); - ITIMER_LOCK(it); if (KSI_ONQ(&it->it_ksi)) { it->it_overrun++; } else { - it->it_ksi.ksi_overrun = it->it_overrun; - it->it_overrun_last = it->it_overrun; - it->it_overrun = 0; - psignal_info(p, &it->it_ksi); + if (it->it_sigev.sigev_notify == SIGEV_THREAD_ID) { + /* XXX + * This is too slow if there are many threads, + * why the world don't have a thread hash table, + * sigh. + */ + FOREACH_THREAD_IN_PROC(p, td) { + if (td->td_tid == + it->it_sigev.sigev_notify_thread_id) + break; + } + if (td != NULL) + tdsignal(td, it->it_ksi.ksi_signo, + &it->it_ksi, SIGTARGET_TD); + else { + /* + * Broken userland code, thread went + * away, disarm the timer. + */ +#if 0 + it->it_overrun++; +#else + ITIMER_LOCK(it); + timespecclear(&it->it_time.it_value); + timespecclear(&it->it_time.it_interval); + callout_stop(&it->it_callout); + ITIMER_UNLOCK(it); +#endif + } + } else { + psignal_info(p, &it->it_ksi); + } } PROC_UNLOCK(p); - ITIMER_UNLOCK(it); } } diff --git a/sys/sys/timers.h b/sys/sys/timers.h index 18434fa501f8..8a2d771685db 100644 --- a/sys/sys/timers.h +++ b/sys/sys/timers.h @@ -47,7 +47,7 @@ struct itimer { struct mtx it_mtx; struct sigevent it_sigev; - struct itimerval it_time; + struct itimerspec it_time; struct proc *it_proc; int it_flags; int it_usecount; @@ -113,6 +113,6 @@ struct kclock { #define ITIMER_EV_EXIT 1 void itimers_event_hook(struct proc *p, int event); - +int itimer_accept(struct proc *p, timer_t tid, ksiginfo_t *ksi); #endif #endif /* !_SYS_TIMERS_H_ */