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.
This commit is contained in:
David Xu 2005-10-30 02:56:08 +00:00
parent 01790d850d
commit 56c06c4b67
3 changed files with 125 additions and 62 deletions

View File

@ -69,6 +69,7 @@ __FBSDID("$FreeBSD$");
#include <sys/sysent.h> #include <sys/sysent.h>
#include <sys/syslog.h> #include <sys/syslog.h>
#include <sys/sysproto.h> #include <sys/sysproto.h>
#include <sys/timers.h>
#include <sys/unistd.h> #include <sys/unistd.h>
#include <sys/wait.h> #include <sys/wait.h>
#include <vm/vm.h> #include <vm/vm.h>
@ -1252,6 +1253,8 @@ kern_sigtimedwait(struct thread *td, sigset_t waitset, ksiginfo_t *ksi,
ksiginfo_init(ksi); ksiginfo_init(ksi);
sigqueue_get(&td->td_sigqueue, sig, ksi); sigqueue_get(&td->td_sigqueue, sig, ksi);
ksi->ksi_signo = sig; ksi->ksi_signo = sig;
if (ksi->ksi_code == SI_TIMER)
itimer_accept(p, ksi->ksi_timerid, ksi);
error = 0; error = 0;
mtx_lock(&ps->ps_mtx); mtx_lock(&ps->ps_mtx);
action = ps->ps_sigact[_SIG_IDX(sig)]; action = ps->ps_sigact[_SIG_IDX(sig)];
@ -2670,7 +2673,8 @@ postsig(sig)
ksiginfo_init(&ksi); ksiginfo_init(&ksi);
sigqueue_get(&td->td_sigqueue, sig, &ksi); sigqueue_get(&td->td_sigqueue, sig, &ksi);
ksi.ksi_signo = sig; ksi.ksi_signo = sig;
if (ksi.ksi_code == SI_TIMER)
itimer_accept(p, ksi.ksi_timerid, &ksi);
action = ps->ps_sigact[_SIG_IDX(sig)]; action = ps->ps_sigact[_SIG_IDX(sig)];
#ifdef KTRACE #ifdef KTRACE
if (KTRPOINT(td, KTR_PSIG)) if (KTRPOINT(td, KTR_PSIG))

View File

@ -88,7 +88,7 @@ static int realtimer_gettime(struct itimer *, struct itimerspec *);
static int realtimer_settime(struct itimer *, int, static int realtimer_settime(struct itimer *, int,
struct itimerspec *, struct itimerspec *); struct itimerspec *, struct itimerspec *);
static int realtimer_delete(struct itimer *); 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_expire(void *);
static void realtimer_event_hook(struct proc *, clockid_t, int event); static void realtimer_event_hook(struct proc *, clockid_t, int event);
static int kern_timer_create(struct thread *, clockid_t, 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 *); int register_posix_clock(int, struct kclock *);
void itimer_fire(struct itimer *it); void itimer_fire(struct itimer *it);
int itimespecfix(struct timespec *ts);
#define CLOCK_CALL(clock, call, arglist) \ #define CLOCK_CALL(clock, call, arglist) \
((*posix_clocks[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 != NULL) {
if (evp->sigev_notify != SIGEV_NONE && if (evp->sigev_notify != SIGEV_NONE &&
evp->sigev_notify != SIGEV_SIGNAL) evp->sigev_notify != SIGEV_SIGNAL &&
evp->sigev_notify != SIGEV_THREAD_ID)
return (EINVAL); 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)) !_SIG_VALID(evp->sigev_signo))
return (EINVAL); return (EINVAL);
} }
@ -971,8 +974,8 @@ kern_timer_create(struct thread *td, clockid_t clock_id,
it->it_flags = 0; it->it_flags = 0;
it->it_usecount = 0; it->it_usecount = 0;
it->it_active = 0; it->it_active = 0;
timevalclear(&it->it_time.it_value); timespecclear(&it->it_time.it_value);
timevalclear(&it->it_time.it_interval); timespecclear(&it->it_time.it_interval);
it->it_overrun = 0; it->it_overrun = 0;
it->it_overrun_last = 0; it->it_overrun_last = 0;
it->it_clockid = clock_id; 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; 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_signo = it->it_sigev.sigev_signo;
it->it_ksi.ksi_code = SI_TIMER; it->it_ksi.ksi_code = SI_TIMER;
it->it_ksi.ksi_value = it->it_sigev.sigev_value; 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); PROC_UNLOCK(p);
error = EINVAL; error = EINVAL;
} else { } else {
PROC_UNLOCK(p);
td->td_retval[0] = it->it_overrun_last; td->td_retval[0] = it->it_overrun_last;
ITIMER_UNLOCK(it); ITIMER_UNLOCK(it);
PROC_UNLOCK(p);
error = 0; error = 0;
} }
return (error); return (error);
@ -1230,18 +1234,14 @@ realtimer_delete(struct itimer *it)
static int static int
realtimer_gettime(struct itimer *it, struct itimerspec *ovalue) realtimer_gettime(struct itimer *it, struct itimerspec *ovalue)
{ {
struct timespec ts; struct timespec cts;
mtx_assert(&it->it_mtx, MA_OWNED); mtx_assert(&it->it_mtx, MA_OWNED);
TIMEVAL_TO_TIMESPEC(&it->it_time.it_value, &ovalue->it_value); realtimer_clocktime(it->it_clockid, &cts);
TIMEVAL_TO_TIMESPEC(&it->it_time.it_interval, &ovalue->it_interval); *ovalue = it->it_time;
if (it->it_clockid == CLOCK_REALTIME)
getnanotime(&ts);
else /* CLOCK_MONOTONIC */
getnanouptime(&ts);
if (ovalue->it_value.tv_sec != 0 || ovalue->it_value.tv_nsec != 0) { 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 || if (ovalue->it_value.tv_sec < 0 ||
(ovalue->it_value.tv_sec == 0 && (ovalue->it_value.tv_sec == 0 &&
ovalue->it_value.tv_nsec == 0)) { ovalue->it_value.tv_nsec == 0)) {
@ -1256,42 +1256,42 @@ static int
realtimer_settime(struct itimer *it, int flags, realtimer_settime(struct itimer *it, int flags,
struct itimerspec *value, struct itimerspec *ovalue) struct itimerspec *value, struct itimerspec *ovalue)
{ {
struct timeval tv, tv2; struct timespec cts, ts;
struct itimerval val; struct timeval tv;
struct itimerspec val;
mtx_assert(&it->it_mtx, MA_OWNED); mtx_assert(&it->it_mtx, MA_OWNED);
TIMESPEC_TO_TIMEVAL(&val.it_value, &value->it_value); val = *value;
TIMESPEC_TO_TIMEVAL(&val.it_interval, &value->it_interval); if (itimespecfix(&val.it_value))
if (itimerfix(&val.it_value))
return (EINVAL); return (EINVAL);
if (timevalisset(&val.it_value)) { if (timespecisset(&val.it_value)) {
if (itimerfix(&val.it_interval)) if (itimespecfix(&val.it_interval))
return (EINVAL); return (EINVAL);
} else { } else {
timevalclear(&val.it_interval); timespecclear(&val.it_interval);
} }
if (ovalue != NULL) if (ovalue != NULL)
realtimer_gettime(it, ovalue); realtimer_gettime(it, ovalue);
it->it_time = val; it->it_time = val;
if (timevalisset(&val.it_value)) { if (timespecisset(&val.it_value)) {
realtimer_clocktime(it->it_clockid, &tv); realtimer_clocktime(it->it_clockid, &cts);
tv2 = val.it_value; ts = val.it_value;
if ((flags & TIMER_ABSTIME) == 0) { if ((flags & TIMER_ABSTIME) == 0) {
/* Convert to absolute time. */ /* Convert to absolute time. */
timevaladd(&it->it_time.it_value, &tv); timespecadd(&it->it_time.it_value, &cts);
} else { } 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. * fix it.
*/ */
} }
callout_reset(&it->it_callout, tvtohz(&tv2), TIMESPEC_TO_TIMEVAL(&tv, &ts);
callout_reset(&it->it_callout, tvtohz(&tv),
realtimer_expire, it); realtimer_expire, it);
} else { } else {
callout_stop(&it->it_callout); callout_stop(&it->it_callout);
@ -1301,12 +1301,40 @@ realtimer_settime(struct itimer *it, int flags,
} }
static void static void
realtimer_clocktime(clockid_t id, struct timeval *tv) realtimer_clocktime(clockid_t id, struct timespec *ts)
{ {
if (id == CLOCK_REALTIME) if (id == CLOCK_REALTIME)
getmicrotime(tv); getnanotime(ts);
else /* CLOCK_MONOTONIC */ 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 static void
@ -1339,41 +1367,44 @@ realtimer_event_hook(struct proc *p, clockid_t clock_id, int event)
static void static void
realtimer_expire(void *arg) realtimer_expire(void *arg)
{ {
struct timeval tv, tv2; struct timespec cts, ts;
struct timeval tv;
struct itimer *it; struct itimer *it;
struct proc *p; struct proc *p;
it = (struct itimer *)arg; it = (struct itimer *)arg;
p = it->it_proc; p = it->it_proc;
realtimer_clocktime(it->it_clockid, &tv); realtimer_clocktime(it->it_clockid, &cts);
/* Only fire if time is reached. */ /* Only fire if time is reached. */
if (timevalcmp(&it->it_time.it_value, &tv, <=)) { if (timespeccmp(&cts, &it->it_time.it_value, >=)) {
if (timevalisset(&it->it_time.it_interval)) { if (timespecisset(&it->it_time.it_interval)) {
timevaladd(&it->it_time.it_value, timespecadd(&it->it_time.it_value,
&it->it_time.it_interval); &it->it_time.it_interval);
while (timevalcmp(&it->it_time.it_value, &tv, <=)) { while (timespeccmp(&cts, &it->it_time.it_value, >=)) {
it->it_overrun++; it->it_overrun++;
timevaladd(&it->it_time.it_value, timespecadd(&it->it_time.it_value,
&it->it_time.it_interval); &it->it_time.it_interval);
} }
} else { } else {
/* single shot timer ? */ /* single shot timer ? */
timevalclear(&it->it_time.it_value); timespecclear(&it->it_time.it_value);
} }
if (timevalisset(&it->it_time.it_value)) { if (timespecisset(&it->it_time.it_value)) {
tv2 = it->it_time.it_value; ts = it->it_time.it_value;
timevalsub(&tv2, &tv); timespecsub(&ts, &cts);
callout_reset(&it->it_callout, tvtohz(&tv2), TIMESPEC_TO_TIMEVAL(&tv, &ts);
callout_reset(&it->it_callout, tvtohz(&tv),
realtimer_expire, it); realtimer_expire, it);
} }
ITIMER_UNLOCK(it); ITIMER_UNLOCK(it);
itimer_fire(it); itimer_fire(it);
ITIMER_LOCK(it); ITIMER_LOCK(it);
} else if (timevalisset(&it->it_time.it_value)) { } else if (timespecisset(&it->it_time.it_value)) {
tv2 = it->it_time.it_value; ts = it->it_time.it_value;
timevalsub(&tv2, &tv); timespecsub(&ts, &cts);
callout_reset(&it->it_callout, tvtohz(&tv2), realtimer_expire, TIMESPEC_TO_TIMEVAL(&tv, &ts);
callout_reset(&it->it_callout, tvtohz(&tv), realtimer_expire,
it); it);
} }
} }
@ -1382,20 +1413,48 @@ void
itimer_fire(struct itimer *it) itimer_fire(struct itimer *it)
{ {
struct proc *p = it->it_proc; 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); PROC_LOCK(p);
ITIMER_LOCK(it);
if (KSI_ONQ(&it->it_ksi)) { if (KSI_ONQ(&it->it_ksi)) {
it->it_overrun++; it->it_overrun++;
} else { } else {
it->it_ksi.ksi_overrun = it->it_overrun; if (it->it_sigev.sigev_notify == SIGEV_THREAD_ID) {
it->it_overrun_last = it->it_overrun; /* XXX
it->it_overrun = 0; * This is too slow if there are many threads,
psignal_info(p, &it->it_ksi); * 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); PROC_UNLOCK(p);
ITIMER_UNLOCK(it);
} }
} }

View File

@ -47,7 +47,7 @@
struct itimer { struct itimer {
struct mtx it_mtx; struct mtx it_mtx;
struct sigevent it_sigev; struct sigevent it_sigev;
struct itimerval it_time; struct itimerspec it_time;
struct proc *it_proc; struct proc *it_proc;
int it_flags; int it_flags;
int it_usecount; int it_usecount;
@ -113,6 +113,6 @@ struct kclock {
#define ITIMER_EV_EXIT 1 #define ITIMER_EV_EXIT 1
void itimers_event_hook(struct proc *p, int event); void itimers_event_hook(struct proc *p, int event);
int itimer_accept(struct proc *p, timer_t tid, ksiginfo_t *ksi);
#endif #endif
#endif /* !_SYS_TIMERS_H_ */ #endif /* !_SYS_TIMERS_H_ */